]>
Commit | Line | Data |
---|---|---|
d75b40d3 | 1 | From 8f0a917911fe19f9911d972fe85c43243f7eaa37 Mon Sep 17 00:00:00 2001 |
daaa955e AM |
2 | From: John Johansen <john.johansen@canonical.com> |
3 | Date: Tue, 18 Jul 2017 23:27:23 -0700 | |
d75b40d3 | 4 | Subject: [PATCH 2/2] apparmor: af_unix mediation |
daaa955e AM |
5 | |
6 | af_socket mediation did not make it into 4.14 so add remaining out | |
7 | of tree patch | |
8 | ||
9 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
d75b40d3 | 10 | Signed-off-by: Seth Forshee <seth.forshee@canonical.com> |
daaa955e AM |
11 | --- |
12 | security/apparmor/Makefile | 3 +- | |
13 | security/apparmor/af_unix.c | 651 ++++++++++++++++++++++++++++++++++++ | |
14 | security/apparmor/apparmorfs.c | 6 + | |
15 | security/apparmor/file.c | 4 +- | |
16 | security/apparmor/include/af_unix.h | 114 +++++++ | |
17 | security/apparmor/include/net.h | 16 +- | |
18 | security/apparmor/include/path.h | 1 + | |
19 | security/apparmor/include/policy.h | 2 +- | |
20 | security/apparmor/lsm.c | 169 ++++++---- | |
21 | security/apparmor/net.c | 174 +++++++++- | |
22 | 10 files changed, 1072 insertions(+), 68 deletions(-) | |
23 | create mode 100644 security/apparmor/af_unix.c | |
24 | create mode 100644 security/apparmor/include/af_unix.h | |
25 | ||
26 | diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile | |
d75b40d3 | 27 | index e7ff2183532a..90c118f39e13 100644 |
daaa955e AM |
28 | --- a/security/apparmor/Makefile |
29 | +++ b/security/apparmor/Makefile | |
d75b40d3 | 30 | @@ -5,7 +5,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o |
daaa955e AM |
31 | |
32 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | |
33 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | |
34 | - resource.o secid.o file.o policy_ns.o label.o mount.o net.o | |
35 | + resource.o secid.o file.o policy_ns.o label.o mount.o net.o \ | |
36 | + af_unix.o | |
37 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o | |
38 | ||
39 | clean-files := capability_names.h rlim_names.h net_names.h | |
40 | diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c | |
41 | new file mode 100644 | |
42 | index 000000000000..c6876db2dbde | |
43 | --- /dev/null | |
44 | +++ b/security/apparmor/af_unix.c | |
45 | @@ -0,0 +1,651 @@ | |
46 | +/* | |
47 | + * AppArmor security module | |
48 | + * | |
49 | + * This file contains AppArmor af_unix fine grained mediation | |
50 | + * | |
51 | + * Copyright 2014 Canonical Ltd. | |
52 | + * | |
53 | + * This program is free software; you can redistribute it and/or | |
54 | + * modify it under the terms of the GNU General Public License as | |
55 | + * published by the Free Software Foundation, version 2 of the | |
56 | + * License. | |
57 | + */ | |
58 | + | |
59 | +#include <net/tcp_states.h> | |
60 | + | |
61 | +#include "include/af_unix.h" | |
62 | +#include "include/apparmor.h" | |
63 | +#include "include/context.h" | |
64 | +#include "include/file.h" | |
65 | +#include "include/label.h" | |
66 | +#include "include/path.h" | |
67 | +#include "include/policy.h" | |
68 | + | |
69 | +static inline struct sock *aa_sock(struct unix_sock *u) | |
70 | +{ | |
71 | + return &u->sk; | |
72 | +} | |
73 | + | |
74 | +static inline int unix_fs_perm(const char *op, u32 mask, struct aa_label *label, | |
75 | + struct unix_sock *u, int flags) | |
76 | +{ | |
77 | + AA_BUG(!label); | |
78 | + AA_BUG(!u); | |
79 | + AA_BUG(!UNIX_FS(aa_sock(u))); | |
80 | + | |
81 | + if (unconfined(label) || !LABEL_MEDIATES(label, AA_CLASS_FILE)) | |
82 | + return 0; | |
83 | + | |
84 | + mask &= NET_FS_PERMS; | |
85 | + if (!u->path.dentry) { | |
86 | + struct path_cond cond = { }; | |
87 | + struct aa_perms perms = { }; | |
88 | + struct aa_profile *profile; | |
89 | + | |
90 | + /* socket path has been cleared because it is being shutdown | |
91 | + * can only fall back to original sun_path request | |
92 | + */ | |
93 | + struct aa_sk_ctx *ctx = SK_CTX(&u->sk); | |
94 | + if (ctx->path.dentry) | |
95 | + return aa_path_perm(op, label, &ctx->path, flags, mask, | |
96 | + &cond); | |
97 | + return fn_for_each_confined(label, profile, | |
98 | + ((flags | profile->path_flags) & PATH_MEDIATE_DELETED) ? | |
99 | + __aa_path_perm(op, profile, | |
100 | + u->addr->name->sun_path, mask, | |
101 | + &cond, flags, &perms) : | |
102 | + aa_audit_file(profile, &nullperms, op, mask, | |
103 | + u->addr->name->sun_path, NULL, | |
104 | + NULL, cond.uid, | |
105 | + "Failed name lookup - " | |
106 | + "deleted entry", -EACCES)); | |
107 | + } else { | |
108 | + /* the sunpath may not be valid for this ns so use the path */ | |
109 | + struct path_cond cond = { u->path.dentry->d_inode->i_uid, | |
110 | + u->path.dentry->d_inode->i_mode | |
111 | + }; | |
112 | + | |
113 | + return aa_path_perm(op, label, &u->path, flags, mask, &cond); | |
114 | + } | |
115 | + | |
116 | + return 0; | |
117 | +} | |
118 | + | |
119 | +/* passing in state returned by PROFILE_MEDIATES_AF */ | |
120 | +static unsigned int match_to_prot(struct aa_profile *profile, | |
121 | + unsigned int state, int type, int protocol, | |
122 | + const char **info) | |
123 | +{ | |
124 | + __be16 buffer[2]; | |
125 | + buffer[0] = cpu_to_be16(type); | |
126 | + buffer[1] = cpu_to_be16(protocol); | |
127 | + state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, | |
128 | + 4); | |
129 | + if (!state) | |
130 | + *info = "failed type and protocol match"; | |
131 | + return state; | |
132 | +} | |
133 | + | |
134 | +static unsigned int match_addr(struct aa_profile *profile, unsigned int state, | |
135 | + struct sockaddr_un *addr, int addrlen) | |
136 | +{ | |
137 | + if (addr) | |
138 | + /* include leading \0 */ | |
139 | + state = aa_dfa_match_len(profile->policy.dfa, state, | |
140 | + addr->sun_path, | |
141 | + unix_addr_len(addrlen)); | |
142 | + else | |
143 | + /* anonymous end point */ | |
144 | + state = aa_dfa_match_len(profile->policy.dfa, state, "\x01", | |
145 | + 1); | |
146 | + /* todo change to out of band */ | |
147 | + state = aa_dfa_null_transition(profile->policy.dfa, state); | |
148 | + return state; | |
149 | +} | |
150 | + | |
151 | +static unsigned int match_to_local(struct aa_profile *profile, | |
152 | + unsigned int state, int type, int protocol, | |
153 | + struct sockaddr_un *addr, int addrlen, | |
154 | + const char **info) | |
155 | +{ | |
156 | + state = match_to_prot(profile, state, type, protocol, info); | |
157 | + if (state) { | |
158 | + state = match_addr(profile, state, addr, addrlen); | |
159 | + if (state) { | |
160 | + /* todo: local label matching */ | |
161 | + state = aa_dfa_null_transition(profile->policy.dfa, | |
162 | + state); | |
163 | + if (!state) | |
164 | + *info = "failed local label match"; | |
165 | + } else | |
166 | + *info = "failed local address match"; | |
167 | + } | |
168 | + | |
169 | + return state; | |
170 | +} | |
171 | + | |
172 | +static unsigned int match_to_sk(struct aa_profile *profile, | |
173 | + unsigned int state, struct unix_sock *u, | |
174 | + const char **info) | |
175 | +{ | |
176 | + struct sockaddr_un *addr = NULL; | |
177 | + int addrlen = 0; | |
178 | + | |
179 | + if (u->addr) { | |
180 | + addr = u->addr->name; | |
181 | + addrlen = u->addr->len; | |
182 | + } | |
183 | + | |
184 | + return match_to_local(profile, state, u->sk.sk_type, u->sk.sk_protocol, | |
185 | + addr, addrlen, info); | |
186 | +} | |
187 | + | |
188 | +#define CMD_ADDR 1 | |
189 | +#define CMD_LISTEN 2 | |
190 | +#define CMD_OPT 4 | |
191 | + | |
192 | +static inline unsigned int match_to_cmd(struct aa_profile *profile, | |
193 | + unsigned int state, struct unix_sock *u, | |
194 | + char cmd, const char **info) | |
195 | +{ | |
196 | + state = match_to_sk(profile, state, u, info); | |
197 | + if (state) { | |
198 | + state = aa_dfa_match_len(profile->policy.dfa, state, &cmd, 1); | |
199 | + if (!state) | |
200 | + *info = "failed cmd selection match"; | |
201 | + } | |
202 | + | |
203 | + return state; | |
204 | +} | |
205 | + | |
206 | +static inline unsigned int match_to_peer(struct aa_profile *profile, | |
207 | + unsigned int state, | |
208 | + struct unix_sock *u, | |
209 | + struct sockaddr_un *peer_addr, | |
210 | + int peer_addrlen, | |
211 | + const char **info) | |
212 | +{ | |
213 | + state = match_to_cmd(profile, state, u, CMD_ADDR, info); | |
214 | + if (state) { | |
215 | + state = match_addr(profile, state, peer_addr, peer_addrlen); | |
216 | + if (!state) | |
217 | + *info = "failed peer address match"; | |
218 | + } | |
219 | + return state; | |
220 | +} | |
221 | + | |
222 | +static int do_perms(struct aa_profile *profile, unsigned int state, u32 request, | |
223 | + struct common_audit_data *sa) | |
224 | +{ | |
225 | + struct aa_perms perms; | |
226 | + | |
227 | + AA_BUG(!profile); | |
228 | + | |
229 | + aa_compute_perms(profile->policy.dfa, state, &perms); | |
230 | + aa_apply_modes_to_perms(profile, &perms); | |
231 | + return aa_check_perms(profile, &perms, request, sa, | |
232 | + audit_net_cb); | |
233 | +} | |
234 | + | |
235 | +static int match_label(struct aa_profile *profile, struct aa_profile *peer, | |
236 | + unsigned int state, u32 request, | |
237 | + struct common_audit_data *sa) | |
238 | +{ | |
239 | + AA_BUG(!profile); | |
240 | + AA_BUG(!peer); | |
241 | + | |
242 | + aad(sa)->peer = &peer->label; | |
243 | + | |
244 | + if (state) { | |
245 | + state = aa_dfa_match(profile->policy.dfa, state, | |
246 | + peer->base.hname); | |
247 | + if (!state) | |
248 | + aad(sa)->info = "failed peer label match"; | |
249 | + } | |
250 | + return do_perms(profile, state, request, sa); | |
251 | +} | |
252 | + | |
253 | + | |
254 | +/* unix sock creation comes before we know if the socket will be an fs | |
255 | + * socket | |
256 | + * v6 - semantics are handled by mapping in profile load | |
257 | + * v7 - semantics require sock create for tasks creating an fs socket. | |
258 | + */ | |
259 | +static int profile_create_perm(struct aa_profile *profile, int family, | |
260 | + int type, int protocol) | |
261 | +{ | |
262 | + unsigned int state; | |
263 | + DEFINE_AUDIT_NET(sa, OP_CREATE, NULL, family, type, protocol); | |
264 | + | |
265 | + AA_BUG(!profile); | |
266 | + AA_BUG(profile_unconfined(profile)); | |
267 | + | |
268 | + if ((state = PROFILE_MEDIATES_AF(profile, AF_UNIX))) { | |
269 | + state = match_to_prot(profile, state, type, protocol, | |
270 | + &aad(&sa)->info); | |
271 | + return do_perms(profile, state, AA_MAY_CREATE, &sa); | |
272 | + } | |
273 | + | |
274 | + return aa_profile_af_perm(profile, &sa, AA_MAY_CREATE, family, type); | |
275 | +} | |
276 | + | |
277 | +int aa_unix_create_perm(struct aa_label *label, int family, int type, | |
278 | + int protocol) | |
279 | +{ | |
280 | + struct aa_profile *profile; | |
281 | + | |
282 | + if (unconfined(label)) | |
283 | + return 0; | |
284 | + | |
285 | + return fn_for_each_confined(label, profile, | |
286 | + profile_create_perm(profile, family, type, protocol)); | |
287 | +} | |
288 | + | |
289 | + | |
290 | +static inline int profile_sk_perm(struct aa_profile *profile, const char *op, | |
291 | + u32 request, struct sock *sk) | |
292 | +{ | |
293 | + unsigned int state; | |
294 | + DEFINE_AUDIT_SK(sa, op, sk); | |
295 | + | |
296 | + AA_BUG(!profile); | |
297 | + AA_BUG(!sk); | |
298 | + AA_BUG(UNIX_FS(sk)); | |
299 | + AA_BUG(profile_unconfined(profile)); | |
300 | + | |
301 | + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); | |
302 | + if (state) { | |
303 | + state = match_to_sk(profile, state, unix_sk(sk), | |
304 | + &aad(&sa)->info); | |
305 | + return do_perms(profile, state, request, &sa); | |
306 | + } | |
307 | + | |
308 | + return aa_profile_af_sk_perm(profile, &sa, request, sk); | |
309 | +} | |
310 | + | |
311 | +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, | |
312 | + struct sock *sk) | |
313 | +{ | |
314 | + struct aa_profile *profile; | |
315 | + | |
316 | + return fn_for_each_confined(label, profile, | |
317 | + profile_sk_perm(profile, op, request, sk)); | |
318 | +} | |
319 | + | |
320 | +static int unix_label_sock_perm(struct aa_label *label, const char *op, u32 request, | |
321 | + struct socket *sock) | |
322 | +{ | |
323 | + if (unconfined(label)) | |
324 | + return 0; | |
325 | + if (UNIX_FS(sock->sk)) | |
326 | + return unix_fs_perm(op, request, label, unix_sk(sock->sk), 0); | |
327 | + | |
328 | + return aa_unix_label_sk_perm(label, op, request, sock->sk); | |
329 | +} | |
330 | + | |
331 | +/* revaliation, get/set attr */ | |
332 | +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock) | |
333 | +{ | |
334 | + struct aa_label *label; | |
335 | + int error; | |
336 | + | |
337 | + label = begin_current_label_crit_section(); | |
338 | + error = unix_label_sock_perm(label, op, request, sock); | |
339 | + end_current_label_crit_section(label); | |
340 | + | |
341 | + return error; | |
342 | +} | |
343 | + | |
344 | +static int profile_bind_perm(struct aa_profile *profile, struct sock *sk, | |
345 | + struct sockaddr *addr, int addrlen) | |
346 | +{ | |
347 | + unsigned int state; | |
348 | + DEFINE_AUDIT_SK(sa, OP_BIND, sk); | |
349 | + | |
350 | + AA_BUG(!profile); | |
351 | + AA_BUG(!sk); | |
352 | + AA_BUG(addr->sa_family != AF_UNIX); | |
353 | + AA_BUG(profile_unconfined(profile)); | |
354 | + AA_BUG(unix_addr_fs(addr, addrlen)); | |
355 | + | |
356 | + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); | |
357 | + if (state) { | |
358 | + /* bind for abstract socket */ | |
359 | + aad(&sa)->net.addr = unix_addr(addr); | |
360 | + aad(&sa)->net.addrlen = addrlen; | |
361 | + | |
362 | + state = match_to_local(profile, state, | |
363 | + sk->sk_type, sk->sk_protocol, | |
364 | + unix_addr(addr), addrlen, | |
365 | + &aad(&sa)->info); | |
366 | + return do_perms(profile, state, AA_MAY_BIND, &sa); | |
367 | + } | |
368 | + | |
369 | + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_BIND, sk); | |
370 | +} | |
371 | + | |
372 | +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, | |
373 | + int addrlen) | |
374 | +{ | |
375 | + struct aa_profile *profile; | |
376 | + struct aa_label *label; | |
377 | + int error = 0; | |
378 | + | |
379 | + label = begin_current_label_crit_section(); | |
380 | + /* fs bind is handled by mknod */ | |
381 | + if (!(unconfined(label) || unix_addr_fs(address, addrlen))) | |
382 | + error = fn_for_each_confined(label, profile, | |
383 | + profile_bind_perm(profile, sock->sk, address, | |
384 | + addrlen)); | |
385 | + end_current_label_crit_section(label); | |
386 | + | |
387 | + return error; | |
388 | +} | |
389 | + | |
390 | +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, | |
391 | + int addrlen) | |
392 | +{ | |
393 | + /* unix connections are covered by the | |
394 | + * - unix_stream_connect (stream) and unix_may_send hooks (dgram) | |
395 | + * - fs connect is handled by open | |
396 | + */ | |
397 | + return 0; | |
398 | +} | |
399 | + | |
400 | +static int profile_listen_perm(struct aa_profile *profile, struct sock *sk, | |
401 | + int backlog) | |
402 | +{ | |
403 | + unsigned int state; | |
404 | + DEFINE_AUDIT_SK(sa, OP_LISTEN, sk); | |
405 | + | |
406 | + AA_BUG(!profile); | |
407 | + AA_BUG(!sk); | |
408 | + AA_BUG(UNIX_FS(sk)); | |
409 | + AA_BUG(profile_unconfined(profile)); | |
410 | + | |
411 | + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); | |
412 | + if (state) { | |
413 | + __be16 b = cpu_to_be16(backlog); | |
414 | + | |
415 | + state = match_to_cmd(profile, state, unix_sk(sk), CMD_LISTEN, | |
416 | + &aad(&sa)->info); | |
417 | + if (state) { | |
418 | + state = aa_dfa_match_len(profile->policy.dfa, state, | |
419 | + (char *) &b, 2); | |
420 | + if (!state) | |
421 | + aad(&sa)->info = "failed listen backlog match"; | |
422 | + } | |
423 | + return do_perms(profile, state, AA_MAY_LISTEN, &sa); | |
424 | + } | |
425 | + | |
426 | + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_LISTEN, sk); | |
427 | +} | |
428 | + | |
429 | +int aa_unix_listen_perm(struct socket *sock, int backlog) | |
430 | +{ | |
431 | + struct aa_profile *profile; | |
432 | + struct aa_label *label; | |
433 | + int error = 0; | |
434 | + | |
435 | + label = begin_current_label_crit_section(); | |
436 | + if (!(unconfined(label) || UNIX_FS(sock->sk))) | |
437 | + error = fn_for_each_confined(label, profile, | |
438 | + profile_listen_perm(profile, sock->sk, | |
439 | + backlog)); | |
440 | + end_current_label_crit_section(label); | |
441 | + | |
442 | + return error; | |
443 | +} | |
444 | + | |
445 | + | |
446 | +static inline int profile_accept_perm(struct aa_profile *profile, | |
447 | + struct sock *sk, | |
448 | + struct sock *newsk) | |
449 | +{ | |
450 | + unsigned int state; | |
451 | + DEFINE_AUDIT_SK(sa, OP_ACCEPT, sk); | |
452 | + | |
453 | + AA_BUG(!profile); | |
454 | + AA_BUG(!sk); | |
455 | + AA_BUG(UNIX_FS(sk)); | |
456 | + AA_BUG(profile_unconfined(profile)); | |
457 | + | |
458 | + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); | |
459 | + if (state) { | |
460 | + state = match_to_sk(profile, state, unix_sk(sk), | |
461 | + &aad(&sa)->info); | |
462 | + return do_perms(profile, state, AA_MAY_ACCEPT, &sa); | |
463 | + } | |
464 | + | |
465 | + return aa_profile_af_sk_perm(profile, &sa, AA_MAY_ACCEPT, sk); | |
466 | +} | |
467 | + | |
468 | +/* ability of sock to connect, not peer address binding */ | |
469 | +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock) | |
470 | +{ | |
471 | + struct aa_profile *profile; | |
472 | + struct aa_label *label; | |
473 | + int error = 0; | |
474 | + | |
475 | + label = begin_current_label_crit_section(); | |
476 | + if (!(unconfined(label) || UNIX_FS(sock->sk))) | |
477 | + error = fn_for_each_confined(label, profile, | |
478 | + profile_accept_perm(profile, sock->sk, | |
479 | + newsock->sk)); | |
480 | + end_current_label_crit_section(label); | |
481 | + | |
482 | + return error; | |
483 | +} | |
484 | + | |
485 | + | |
486 | +/* dgram handled by unix_may_sendmsg, right to send on stream done at connect | |
487 | + * could do per msg unix_stream here | |
488 | + */ | |
489 | +/* sendmsg, recvmsg */ | |
490 | +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, | |
491 | + struct msghdr *msg, int size) | |
492 | +{ | |
493 | + return 0; | |
494 | +} | |
495 | + | |
496 | + | |
497 | +static int profile_opt_perm(struct aa_profile *profile, const char *op, u32 request, | |
498 | + struct sock *sk, int level, int optname) | |
499 | +{ | |
500 | + unsigned int state; | |
501 | + DEFINE_AUDIT_SK(sa, op, sk); | |
502 | + | |
503 | + AA_BUG(!profile); | |
504 | + AA_BUG(!sk); | |
505 | + AA_BUG(UNIX_FS(sk)); | |
506 | + AA_BUG(profile_unconfined(profile)); | |
507 | + | |
508 | + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); | |
509 | + if (state) { | |
510 | + __be16 b = cpu_to_be16(optname); | |
511 | + | |
512 | + state = match_to_cmd(profile, state, unix_sk(sk), CMD_OPT, | |
513 | + &aad(&sa)->info); | |
514 | + if (state) { | |
515 | + state = aa_dfa_match_len(profile->policy.dfa, state, | |
516 | + (char *) &b, 2); | |
517 | + if (!state) | |
518 | + aad(&sa)->info = "failed sockopt match"; | |
519 | + } | |
520 | + return do_perms(profile, state, request, &sa); | |
521 | + } | |
522 | + | |
523 | + return aa_profile_af_sk_perm(profile, &sa, request, sk); | |
524 | +} | |
525 | + | |
526 | +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, | |
527 | + int optname) | |
528 | +{ | |
529 | + struct aa_profile *profile; | |
530 | + struct aa_label *label; | |
531 | + int error = 0; | |
532 | + | |
533 | + label = begin_current_label_crit_section(); | |
534 | + if (!(unconfined(label) || UNIX_FS(sock->sk))) | |
535 | + error = fn_for_each_confined(label, profile, | |
536 | + profile_opt_perm(profile, op, request, | |
537 | + sock->sk, level, optname)); | |
538 | + end_current_label_crit_section(label); | |
539 | + | |
540 | + return error; | |
541 | +} | |
542 | + | |
543 | +/* null peer_label is allowed, in which case the peer_sk label is used */ | |
544 | +static int profile_peer_perm(struct aa_profile *profile, const char *op, u32 request, | |
545 | + struct sock *sk, struct sock *peer_sk, | |
546 | + struct aa_label *peer_label, | |
547 | + struct common_audit_data *sa) | |
548 | +{ | |
549 | + unsigned int state; | |
550 | + | |
551 | + AA_BUG(!profile); | |
552 | + AA_BUG(profile_unconfined(profile)); | |
553 | + AA_BUG(!sk); | |
554 | + AA_BUG(!peer_sk); | |
555 | + AA_BUG(UNIX_FS(peer_sk)); | |
556 | + | |
557 | + state = PROFILE_MEDIATES_AF(profile, AF_UNIX); | |
558 | + if (state) { | |
559 | + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); | |
560 | + struct aa_profile *peerp; | |
561 | + struct sockaddr_un *addr = NULL; | |
562 | + int len = 0; | |
563 | + if (unix_sk(peer_sk)->addr) { | |
564 | + addr = unix_sk(peer_sk)->addr->name; | |
565 | + len = unix_sk(peer_sk)->addr->len; | |
566 | + } | |
567 | + state = match_to_peer(profile, state, unix_sk(sk), | |
568 | + addr, len, &aad(sa)->info); | |
569 | + if (!peer_label) | |
570 | + peer_label = peer_ctx->label; | |
571 | + return fn_for_each_in_ns(peer_label, peerp, | |
572 | + match_label(profile, peerp, state, request, | |
573 | + sa)); | |
574 | + } | |
575 | + | |
576 | + return aa_profile_af_sk_perm(profile, sa, request, sk); | |
577 | +} | |
578 | + | |
579 | +/** | |
580 | + * | |
581 | + * Requires: lock held on both @sk and @peer_sk | |
582 | + */ | |
583 | +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, | |
584 | + struct sock *sk, struct sock *peer_sk, | |
585 | + struct aa_label *peer_label) | |
586 | +{ | |
587 | + struct unix_sock *peeru = unix_sk(peer_sk); | |
588 | + struct unix_sock *u = unix_sk(sk); | |
589 | + | |
590 | + AA_BUG(!label); | |
591 | + AA_BUG(!sk); | |
592 | + AA_BUG(!peer_sk); | |
593 | + | |
594 | + if (UNIX_FS(aa_sock(peeru))) | |
595 | + return unix_fs_perm(op, request, label, peeru, 0); | |
596 | + else if (UNIX_FS(aa_sock(u))) | |
597 | + return unix_fs_perm(op, request, label, u, 0); | |
598 | + else { | |
599 | + struct aa_profile *profile; | |
600 | + DEFINE_AUDIT_SK(sa, op, sk); | |
601 | + aad(&sa)->net.peer_sk = peer_sk; | |
602 | + | |
603 | + /* TODO: ns!!! */ | |
604 | + if (!net_eq(sock_net(sk), sock_net(peer_sk))) { | |
605 | + ; | |
606 | + } | |
607 | + | |
608 | + if (unconfined(label)) | |
609 | + return 0; | |
610 | + | |
611 | + return fn_for_each_confined(label, profile, | |
612 | + profile_peer_perm(profile, op, request, sk, | |
613 | + peer_sk, peer_label, &sa)); | |
614 | + } | |
615 | +} | |
616 | + | |
617 | + | |
618 | +/* from net/unix/af_unix.c */ | |
619 | +static void unix_state_double_lock(struct sock *sk1, struct sock *sk2) | |
620 | +{ | |
621 | + if (unlikely(sk1 == sk2) || !sk2) { | |
622 | + unix_state_lock(sk1); | |
623 | + return; | |
624 | + } | |
625 | + if (sk1 < sk2) { | |
626 | + unix_state_lock(sk1); | |
627 | + unix_state_lock_nested(sk2); | |
628 | + } else { | |
629 | + unix_state_lock(sk2); | |
630 | + unix_state_lock_nested(sk1); | |
631 | + } | |
632 | +} | |
633 | + | |
634 | +static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2) | |
635 | +{ | |
636 | + if (unlikely(sk1 == sk2) || !sk2) { | |
637 | + unix_state_unlock(sk1); | |
638 | + return; | |
639 | + } | |
640 | + unix_state_unlock(sk1); | |
641 | + unix_state_unlock(sk2); | |
642 | +} | |
643 | + | |
644 | +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, | |
645 | + struct socket *sock) | |
646 | +{ | |
647 | + struct sock *peer_sk = NULL; | |
648 | + u32 sk_req = request & ~NET_PEER_MASK; | |
649 | + int error = 0; | |
650 | + | |
651 | + AA_BUG(!label); | |
652 | + AA_BUG(!sock); | |
653 | + AA_BUG(!sock->sk); | |
654 | + AA_BUG(sock->sk->sk_family != AF_UNIX); | |
655 | + | |
656 | + /* TODO: update sock label with new task label */ | |
657 | + unix_state_lock(sock->sk); | |
658 | + peer_sk = unix_peer(sock->sk); | |
659 | + if (peer_sk) | |
660 | + sock_hold(peer_sk); | |
661 | + if (!unix_connected(sock) && sk_req) { | |
662 | + error = unix_label_sock_perm(label, op, sk_req, sock); | |
663 | + if (!error) { | |
664 | + // update label | |
665 | + } | |
666 | + } | |
667 | + unix_state_unlock(sock->sk); | |
668 | + if (!peer_sk) | |
669 | + return error; | |
670 | + | |
671 | + unix_state_double_lock(sock->sk, peer_sk); | |
672 | + if (UNIX_FS(sock->sk)) { | |
673 | + error = unix_fs_perm(op, request, label, unix_sk(sock->sk), | |
674 | + PATH_SOCK_COND); | |
675 | + } else if (UNIX_FS(peer_sk)) { | |
676 | + error = unix_fs_perm(op, request, label, unix_sk(peer_sk), | |
677 | + PATH_SOCK_COND); | |
678 | + } else { | |
679 | + struct aa_sk_ctx *pctx = SK_CTX(peer_sk); | |
680 | + if (sk_req) | |
681 | + error = aa_unix_label_sk_perm(label, op, sk_req, | |
682 | + sock->sk); | |
683 | + last_error(error, | |
684 | + xcheck(aa_unix_peer_perm(label, op, | |
685 | + MAY_READ | MAY_WRITE, | |
686 | + sock->sk, peer_sk, NULL), | |
687 | + aa_unix_peer_perm(pctx->label, op, | |
688 | + MAY_READ | MAY_WRITE, | |
689 | + peer_sk, sock->sk, label))); | |
690 | + } | |
691 | + | |
692 | + unix_state_double_unlock(sock->sk, peer_sk); | |
693 | + sock_put(peer_sk); | |
694 | + | |
695 | + return error; | |
696 | +} | |
697 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c | |
d75b40d3 | 698 | index 694c4f48a975..850c401502f1 100644 |
daaa955e AM |
699 | --- a/security/apparmor/apparmorfs.c |
700 | +++ b/security/apparmor/apparmorfs.c | |
701 | @@ -2187,6 +2187,11 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = { | |
702 | { } | |
703 | }; | |
704 | ||
705 | +static struct aa_sfs_entry aa_sfs_entry_dbus[] = { | |
706 | + AA_SFS_FILE_STRING("mask", "acquire send receive"), | |
707 | + { } | |
708 | +}; | |
709 | + | |
710 | static struct aa_sfs_entry aa_sfs_entry_query_label[] = { | |
711 | AA_SFS_FILE_STRING("perms", "allow deny audit quiet"), | |
712 | AA_SFS_FILE_BOOLEAN("data", 1), | |
713 | @@ -2210,6 +2215,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { | |
714 | AA_SFS_DIR("caps", aa_sfs_entry_caps), | |
715 | AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), | |
716 | AA_SFS_DIR("signal", aa_sfs_entry_signal), | |
717 | + AA_SFS_DIR("dbus", aa_sfs_entry_dbus), | |
718 | AA_SFS_DIR("query", aa_sfs_entry_query), | |
719 | { } | |
720 | }; | |
721 | diff --git a/security/apparmor/file.c b/security/apparmor/file.c | |
d75b40d3 | 722 | index 86d57e56fabe..348c9ff3da4e 100644 |
daaa955e AM |
723 | --- a/security/apparmor/file.c |
724 | +++ b/security/apparmor/file.c | |
725 | @@ -16,6 +16,7 @@ | |
726 | #include <linux/fdtable.h> | |
727 | #include <linux/file.h> | |
728 | ||
729 | +#include "include/af_unix.h" | |
730 | #include "include/apparmor.h" | |
731 | #include "include/audit.h" | |
732 | #include "include/context.h" | |
d75b40d3 | 733 | @@ -283,7 +284,8 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, |
daaa955e AM |
734 | { |
735 | int e = 0; | |
736 | ||
737 | - if (profile_unconfined(profile)) | |
738 | + if (profile_unconfined(profile) || | |
739 | + ((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX))) | |
740 | return 0; | |
741 | aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms); | |
742 | if (request & ~perms->allow) | |
743 | diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h | |
744 | new file mode 100644 | |
745 | index 000000000000..d1b7f2316be4 | |
746 | --- /dev/null | |
747 | +++ b/security/apparmor/include/af_unix.h | |
748 | @@ -0,0 +1,114 @@ | |
749 | +/* | |
750 | + * AppArmor security module | |
751 | + * | |
752 | + * This file contains AppArmor af_unix fine grained mediation | |
753 | + * | |
754 | + * Copyright 2014 Canonical Ltd. | |
755 | + * | |
756 | + * This program is free software; you can redistribute it and/or | |
757 | + * modify it under the terms of the GNU General Public License as | |
758 | + * published by the Free Software Foundation, version 2 of the | |
759 | + * License. | |
760 | + */ | |
761 | +#ifndef __AA_AF_UNIX_H | |
762 | + | |
763 | +#include <net/af_unix.h> | |
764 | + | |
765 | +#include "label.h" | |
766 | +//#include "include/net.h" | |
767 | + | |
768 | +#define unix_addr_len(L) ((L) - sizeof(sa_family_t)) | |
769 | +#define unix_abstract_name_len(L) (unix_addr_len(L) - 1) | |
770 | +#define unix_abstract_len(U) (unix_abstract_name_len((U)->addr->len)) | |
771 | +#define addr_unix_abstract_name(B) ((B)[0] == 0) | |
772 | +#define addr_unix_anonymous(U) (addr_unix_len(U) <= 0) | |
773 | +#define addr_unix_abstract(U) (!addr_unix_anonymous(U) && addr_unix_abstract_name((U)->addr)) | |
774 | +//#define unix_addr_fs(U) (!unix_addr_anonymous(U) && !unix_addr_abstract_name((U)->addr)) | |
775 | + | |
776 | +#define unix_addr(A) ((struct sockaddr_un *)(A)) | |
777 | +#define unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0) | |
778 | +#define unix_addr_fs(A, L) (!unix_addr_anon(A, L) && !addr_unix_abstract_name(unix_addr(A)->sun_path)) | |
779 | + | |
780 | +#define UNIX_ANONYMOUS(U) (!unix_sk(U)->addr) | |
781 | +/* from net/unix/af_unix.c */ | |
782 | +#define UNIX_ABSTRACT(U) (!UNIX_ANONYMOUS(U) && \ | |
783 | + unix_sk(U)->addr->hash < UNIX_HASH_SIZE) | |
784 | +#define UNIX_FS(U) (!UNIX_ANONYMOUS(U) && unix_sk(U)->addr->name->sun_path[0]) | |
785 | +#define unix_peer(sk) (unix_sk(sk)->peer) | |
786 | +#define unix_connected(S) ((S)->state == SS_CONNECTED) | |
787 | + | |
788 | +static inline void print_unix_addr(struct sockaddr_un *A, int L) | |
789 | +{ | |
790 | + char *buf = (A) ? (char *) &(A)->sun_path : NULL; | |
791 | + int len = unix_addr_len(L); | |
792 | + if (!buf || len <= 0) | |
793 | + printk(" <anonymous>"); | |
794 | + else if (buf[0]) | |
795 | + printk(" %s", buf); | |
796 | + else | |
797 | + /* abstract name len includes leading \0 */ | |
798 | + printk(" %d @%.*s", len - 1, len - 1, buf+1); | |
799 | +}; | |
800 | + | |
801 | +/* | |
802 | + printk("%s: %s: f %d, t %d, p %d", __FUNCTION__, \ | |
803 | + #SK , \ | |
804 | +*/ | |
805 | +#define print_unix_sk(SK) \ | |
806 | +do { \ | |
807 | + struct unix_sock *u = unix_sk(SK); \ | |
808 | + printk("%s: f %d, t %d, p %d", #SK , \ | |
809 | + (SK)->sk_family, (SK)->sk_type, (SK)->sk_protocol); \ | |
810 | + if (u->addr) \ | |
811 | + print_unix_addr(u->addr->name, u->addr->len); \ | |
812 | + else \ | |
813 | + print_unix_addr(NULL, sizeof(sa_family_t)); \ | |
814 | + /* printk("\n");*/ \ | |
815 | +} while (0) | |
816 | + | |
817 | +#define print_sk(SK) \ | |
818 | +do { \ | |
819 | + if (!(SK)) { \ | |
820 | + printk("%s: %s is null\n", __FUNCTION__, #SK); \ | |
821 | + } else if ((SK)->sk_family == PF_UNIX) { \ | |
822 | + print_unix_sk(SK); \ | |
823 | + printk("\n"); \ | |
824 | + } else { \ | |
825 | + printk("%s: %s: family %d\n", __FUNCTION__, #SK , \ | |
826 | + (SK)->sk_family); \ | |
827 | + } \ | |
828 | +} while (0) | |
829 | + | |
830 | +#define print_sock_addr(U) \ | |
831 | +do { \ | |
832 | + printk("%s:\n", __FUNCTION__); \ | |
833 | + printk(" sock %s:", sock_ctx && sock_ctx->label ? aa_label_printk(sock_ctx->label, GFP_ATOMIC); : "<null>"); print_sk(sock); \ | |
834 | + printk(" other %s:", other_ctx && other_ctx->label ? aa_label_printk(other_ctx->label, GFP_ATOMIC); : "<null>"); print_sk(other); \ | |
835 | + printk(" new %s", new_ctx && new_ctx->label ? aa_label_printk(new_ctx->label, GFP_ATOMIC); : "<null>"); print_sk(newsk); \ | |
836 | +} while (0) | |
837 | + | |
838 | + | |
839 | + | |
840 | + | |
841 | +int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, | |
842 | + struct sock *sk, struct sock *peer_sk, | |
843 | + struct aa_label *peer_label); | |
844 | +int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, | |
845 | + struct sock *sk); | |
846 | +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock); | |
847 | +int aa_unix_create_perm(struct aa_label *label, int family, int type, | |
848 | + int protocol); | |
849 | +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, | |
850 | + int addrlen); | |
851 | +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, | |
852 | + int addrlen); | |
853 | +int aa_unix_listen_perm(struct socket *sock, int backlog); | |
854 | +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock); | |
855 | +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, | |
856 | + struct msghdr *msg, int size); | |
857 | +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, | |
858 | + int optname); | |
859 | +int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, | |
860 | + struct socket *sock); | |
861 | + | |
862 | +#endif /* __AA_AF_UNIX_H */ | |
863 | diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h | |
864 | index 140c8efcf364..0ae45240c352 100644 | |
865 | --- a/security/apparmor/include/net.h | |
866 | +++ b/security/apparmor/include/net.h | |
867 | @@ -90,8 +90,6 @@ extern struct aa_sfs_entry aa_sfs_entry_network[]; | |
868 | void audit_net_cb(struct audit_buffer *ab, void *va); | |
869 | int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, | |
870 | u32 request, u16 family, int type); | |
871 | -int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, | |
872 | - int type, int protocol); | |
873 | static inline int aa_profile_af_sk_perm(struct aa_profile *profile, | |
874 | struct common_audit_data *sa, | |
875 | u32 request, | |
876 | @@ -100,8 +98,20 @@ static inline int aa_profile_af_sk_perm(struct aa_profile *profile, | |
877 | return aa_profile_af_perm(profile, sa, request, sk->sk_family, | |
878 | sk->sk_type); | |
879 | } | |
880 | -int aa_sk_perm(const char *op, u32 request, struct sock *sk); | |
881 | ||
882 | +int aa_sock_perm(const char *op, u32 request, struct socket *sock); | |
883 | +int aa_sock_create_perm(struct aa_label *label, int family, int type, | |
884 | + int protocol); | |
885 | +int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, | |
886 | + int addrlen); | |
887 | +int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, | |
888 | + int addrlen); | |
889 | +int aa_sock_listen_perm(struct socket *sock, int backlog); | |
890 | +int aa_sock_accept_perm(struct socket *sock, struct socket *newsock); | |
891 | +int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, | |
892 | + struct msghdr *msg, int size); | |
893 | +int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, | |
894 | + int optname); | |
895 | int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, | |
896 | struct socket *sock); | |
897 | ||
898 | diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h | |
899 | index 05fb3305671e..26762db2207d 100644 | |
900 | --- a/security/apparmor/include/path.h | |
901 | +++ b/security/apparmor/include/path.h | |
902 | @@ -18,6 +18,7 @@ | |
903 | ||
904 | enum path_flags { | |
905 | PATH_IS_DIR = 0x1, /* path is a directory */ | |
906 | + PATH_SOCK_COND = 0x2, | |
907 | PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ | |
908 | PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ | |
909 | PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ | |
910 | diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h | |
911 | index 4364088a0b9e..26660a1a50b0 100644 | |
912 | --- a/security/apparmor/include/policy.h | |
913 | +++ b/security/apparmor/include/policy.h | |
914 | @@ -226,7 +226,7 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, | |
915 | static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, | |
916 | u16 AF) { | |
917 | unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); | |
918 | - u16 be_af = cpu_to_be16(AF); | |
919 | + __be16 be_af = cpu_to_be16(AF); | |
920 | ||
921 | if (!state) | |
922 | return 0; | |
923 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c | |
d75b40d3 | 924 | index 0cd717614fd0..245c98ef311e 100644 |
daaa955e AM |
925 | --- a/security/apparmor/lsm.c |
926 | +++ b/security/apparmor/lsm.c | |
927 | @@ -26,6 +26,7 @@ | |
928 | #include <linux/kmemleak.h> | |
929 | #include <net/sock.h> | |
930 | ||
931 | +#include "include/af_unix.h" | |
932 | #include "include/apparmor.h" | |
933 | #include "include/apparmorfs.h" | |
934 | #include "include/audit.h" | |
935 | @@ -782,16 +783,96 @@ static void apparmor_sk_clone_security(const struct sock *sk, | |
936 | path_get(&new->path); | |
937 | } | |
938 | ||
939 | -static int aa_sock_create_perm(struct aa_label *label, int family, int type, | |
940 | - int protocol) | |
941 | +static struct path *UNIX_FS_CONN_PATH(struct sock *sk, struct sock *newsk) | |
942 | { | |
943 | - AA_BUG(!label); | |
944 | - AA_BUG(in_interrupt()); | |
945 | + if (sk->sk_family == PF_UNIX && UNIX_FS(sk)) | |
946 | + return &unix_sk(sk)->path; | |
947 | + else if (newsk->sk_family == PF_UNIX && UNIX_FS(newsk)) | |
948 | + return &unix_sk(newsk)->path; | |
949 | + return NULL; | |
950 | +} | |
951 | + | |
952 | +/** | |
953 | + * apparmor_unix_stream_connect - check perms before making unix domain conn | |
954 | + * | |
955 | + * peer is locked when this hook is called | |
956 | + */ | |
957 | +static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk, | |
958 | + struct sock *newsk) | |
959 | +{ | |
960 | + struct aa_sk_ctx *sk_ctx = SK_CTX(sk); | |
961 | + struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); | |
962 | + struct aa_sk_ctx *new_ctx = SK_CTX(newsk); | |
963 | + struct aa_label *label; | |
964 | + struct path *path; | |
965 | + int error; | |
966 | ||
967 | - return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, | |
968 | - protocol); | |
969 | + label = __begin_current_label_crit_section(); | |
970 | + error = aa_unix_peer_perm(label, OP_CONNECT, | |
971 | + (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE), | |
972 | + sk, peer_sk, NULL); | |
973 | + if (!UNIX_FS(peer_sk)) { | |
974 | + last_error(error, | |
975 | + aa_unix_peer_perm(peer_ctx->label, OP_CONNECT, | |
976 | + (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE), | |
977 | + peer_sk, sk, label)); | |
978 | + } | |
979 | + __end_current_label_crit_section(label); | |
980 | + | |
981 | + if (error) | |
982 | + return error; | |
983 | + | |
984 | + /* label newsk if it wasn't labeled in post_create. Normally this | |
985 | + * would be done in sock_graft, but because we are directly looking | |
986 | + * at the peer_sk to obtain peer_labeling for unix socks this | |
987 | + * does not work | |
988 | + */ | |
989 | + if (!new_ctx->label) | |
990 | + new_ctx->label = aa_get_label(peer_ctx->label); | |
991 | + | |
992 | + /* Cross reference the peer labels for SO_PEERSEC */ | |
993 | + if (new_ctx->peer) | |
994 | + aa_put_label(new_ctx->peer); | |
995 | + | |
996 | + if (sk_ctx->peer) | |
997 | + aa_put_label(sk_ctx->peer); | |
998 | + | |
999 | + new_ctx->peer = aa_get_label(sk_ctx->label); | |
1000 | + sk_ctx->peer = aa_get_label(peer_ctx->label); | |
1001 | + | |
1002 | + path = UNIX_FS_CONN_PATH(sk, peer_sk); | |
1003 | + if (path) { | |
1004 | + new_ctx->path = *path; | |
1005 | + sk_ctx->path = *path; | |
1006 | + path_get(path); | |
1007 | + path_get(path); | |
1008 | + } | |
1009 | + return 0; | |
1010 | } | |
1011 | ||
1012 | +/** | |
1013 | + * apparmor_unix_may_send - check perms before conn or sending unix dgrams | |
1014 | + * | |
1015 | + * other is locked when this hook is called | |
1016 | + * | |
1017 | + * dgram connect calls may_send, peer setup but path not copied????? | |
1018 | + */ | |
1019 | +static int apparmor_unix_may_send(struct socket *sock, struct socket *peer) | |
1020 | +{ | |
1021 | + struct aa_sk_ctx *peer_ctx = SK_CTX(peer->sk); | |
1022 | + struct aa_label *label; | |
1023 | + int error; | |
1024 | + | |
1025 | + label = __begin_current_label_crit_section(); | |
1026 | + error = xcheck(aa_unix_peer_perm(label, OP_SENDMSG, AA_MAY_SEND, | |
1027 | + sock->sk, peer->sk, NULL), | |
1028 | + aa_unix_peer_perm(peer_ctx->label, OP_SENDMSG, | |
1029 | + AA_MAY_RECEIVE, | |
1030 | + peer->sk, sock->sk, label)); | |
1031 | + __end_current_label_crit_section(label); | |
1032 | + | |
1033 | + return error; | |
1034 | +} | |
1035 | ||
1036 | /** | |
1037 | * apparmor_socket_create - check perms before creating a new socket | |
1038 | @@ -849,12 +930,7 @@ static int apparmor_socket_post_create(struct socket *sock, int family, | |
1039 | static int apparmor_socket_bind(struct socket *sock, | |
1040 | struct sockaddr *address, int addrlen) | |
1041 | { | |
1042 | - AA_BUG(!sock); | |
1043 | - AA_BUG(!sock->sk); | |
1044 | - AA_BUG(!address); | |
1045 | - AA_BUG(in_interrupt()); | |
1046 | - | |
1047 | - return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); | |
1048 | + return aa_sock_bind_perm(sock, address, addrlen); | |
1049 | } | |
1050 | ||
1051 | /** | |
1052 | @@ -863,12 +939,7 @@ static int apparmor_socket_bind(struct socket *sock, | |
1053 | static int apparmor_socket_connect(struct socket *sock, | |
1054 | struct sockaddr *address, int addrlen) | |
1055 | { | |
1056 | - AA_BUG(!sock); | |
1057 | - AA_BUG(!sock->sk); | |
1058 | - AA_BUG(!address); | |
1059 | - AA_BUG(in_interrupt()); | |
1060 | - | |
1061 | - return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); | |
1062 | + return aa_sock_connect_perm(sock, address, addrlen); | |
1063 | } | |
1064 | ||
1065 | /** | |
1066 | @@ -876,11 +947,7 @@ static int apparmor_socket_connect(struct socket *sock, | |
1067 | */ | |
1068 | static int apparmor_socket_listen(struct socket *sock, int backlog) | |
1069 | { | |
1070 | - AA_BUG(!sock); | |
1071 | - AA_BUG(!sock->sk); | |
1072 | - AA_BUG(in_interrupt()); | |
1073 | - | |
1074 | - return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); | |
1075 | + return aa_sock_listen_perm(sock, backlog); | |
1076 | } | |
1077 | ||
1078 | /** | |
1079 | @@ -891,23 +958,7 @@ static int apparmor_socket_listen(struct socket *sock, int backlog) | |
1080 | */ | |
1081 | static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) | |
1082 | { | |
1083 | - AA_BUG(!sock); | |
1084 | - AA_BUG(!sock->sk); | |
1085 | - AA_BUG(!newsock); | |
1086 | - AA_BUG(in_interrupt()); | |
1087 | - | |
1088 | - return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); | |
1089 | -} | |
1090 | - | |
1091 | -static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, | |
1092 | - struct msghdr *msg, int size) | |
1093 | -{ | |
1094 | - AA_BUG(!sock); | |
1095 | - AA_BUG(!sock->sk); | |
1096 | - AA_BUG(!msg); | |
1097 | - AA_BUG(in_interrupt()); | |
1098 | - | |
1099 | - return aa_sk_perm(op, request, sock->sk); | |
1100 | + return aa_sock_accept_perm(sock, newsock); | |
1101 | } | |
1102 | ||
1103 | /** | |
1104 | @@ -928,16 +979,6 @@ static int apparmor_socket_recvmsg(struct socket *sock, | |
1105 | return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); | |
1106 | } | |
1107 | ||
1108 | -/* revaliation, get/set attr, shutdown */ | |
1109 | -static int aa_sock_perm(const char *op, u32 request, struct socket *sock) | |
1110 | -{ | |
1111 | - AA_BUG(!sock); | |
1112 | - AA_BUG(!sock->sk); | |
1113 | - AA_BUG(in_interrupt()); | |
1114 | - | |
1115 | - return aa_sk_perm(op, request, sock->sk); | |
1116 | -} | |
1117 | - | |
1118 | /** | |
1119 | * apparmor_socket_getsockname - check perms before getting the local address | |
1120 | */ | |
1121 | @@ -954,17 +995,6 @@ static int apparmor_socket_getpeername(struct socket *sock) | |
1122 | return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); | |
1123 | } | |
1124 | ||
1125 | -/* revaliation, get/set attr, opt */ | |
1126 | -static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, | |
1127 | - int level, int optname) | |
1128 | -{ | |
1129 | - AA_BUG(!sock); | |
1130 | - AA_BUG(!sock->sk); | |
1131 | - AA_BUG(in_interrupt()); | |
1132 | - | |
1133 | - return aa_sk_perm(op, request, sock->sk); | |
1134 | -} | |
1135 | - | |
1136 | /** | |
1137 | * apparmor_getsockopt - check perms before getting socket options | |
1138 | */ | |
1139 | @@ -1009,11 +1039,25 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | |
1140 | ||
1141 | static struct aa_label *sk_peer_label(struct sock *sk) | |
1142 | { | |
1143 | + struct sock *peer_sk; | |
1144 | struct aa_sk_ctx *ctx = SK_CTX(sk); | |
1145 | ||
1146 | if (ctx->peer) | |
1147 | return ctx->peer; | |
1148 | ||
1149 | + if (sk->sk_family != PF_UNIX) | |
1150 | + return ERR_PTR(-ENOPROTOOPT); | |
1151 | + | |
1152 | + /* check for sockpair peering which does not go through | |
1153 | + * security_unix_stream_connect | |
1154 | + */ | |
1155 | + peer_sk = unix_peer(sk); | |
1156 | + if (peer_sk) { | |
1157 | + ctx = SK_CTX(peer_sk); | |
1158 | + if (ctx->label) | |
1159 | + return ctx->label; | |
1160 | + } | |
1161 | + | |
1162 | return ERR_PTR(-ENOPROTOOPT); | |
1163 | } | |
1164 | ||
1165 | @@ -1137,6 +1181,9 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { | |
1166 | LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), | |
1167 | LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), | |
1168 | ||
1169 | + LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect), | |
1170 | + LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send), | |
1171 | + | |
1172 | LSM_HOOK_INIT(socket_create, apparmor_socket_create), | |
1173 | LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), | |
1174 | LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), | |
1175 | diff --git a/security/apparmor/net.c b/security/apparmor/net.c | |
1176 | index 33d54435f8d6..dd1953b08e58 100644 | |
1177 | --- a/security/apparmor/net.c | |
1178 | +++ b/security/apparmor/net.c | |
1179 | @@ -12,6 +12,7 @@ | |
1180 | * License. | |
1181 | */ | |
1182 | ||
1183 | +#include "include/af_unix.h" | |
1184 | #include "include/apparmor.h" | |
1185 | #include "include/audit.h" | |
1186 | #include "include/context.h" | |
1187 | @@ -24,6 +25,7 @@ | |
1188 | ||
1189 | struct aa_sfs_entry aa_sfs_entry_network[] = { | |
1190 | AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), | |
1191 | + AA_SFS_FILE_BOOLEAN("af_unix", 1), | |
1192 | { } | |
1193 | }; | |
1194 | ||
1195 | @@ -69,6 +71,36 @@ static const char * const net_mask_names[] = { | |
1196 | "unknown", | |
1197 | }; | |
1198 | ||
1199 | +static void audit_unix_addr(struct audit_buffer *ab, const char *str, | |
1200 | + struct sockaddr_un *addr, int addrlen) | |
1201 | +{ | |
1202 | + int len = unix_addr_len(addrlen); | |
1203 | + | |
1204 | + if (!addr || len <= 0) { | |
1205 | + audit_log_format(ab, " %s=none", str); | |
1206 | + } else if (addr->sun_path[0]) { | |
1207 | + audit_log_format(ab, " %s=", str); | |
1208 | + audit_log_untrustedstring(ab, addr->sun_path); | |
1209 | + } else { | |
1210 | + audit_log_format(ab, " %s=\"@", str); | |
1211 | + if (audit_string_contains_control(&addr->sun_path[1], len - 1)) | |
1212 | + audit_log_n_hex(ab, &addr->sun_path[1], len - 1); | |
1213 | + else | |
1214 | + audit_log_format(ab, "%.*s", len - 1, | |
1215 | + &addr->sun_path[1]); | |
1216 | + audit_log_format(ab, "\""); | |
1217 | + } | |
1218 | +} | |
1219 | + | |
1220 | +static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str, | |
1221 | + struct sock *sk) | |
1222 | +{ | |
1223 | + struct unix_sock *u = unix_sk(sk); | |
1224 | + if (u && u->addr) | |
1225 | + audit_unix_addr(ab, str, u->addr->name, u->addr->len); | |
1226 | + else | |
1227 | + audit_unix_addr(ab, str, NULL, 0); | |
1228 | +} | |
1229 | ||
1230 | /* audit callback for net specific fields */ | |
1231 | void audit_net_cb(struct audit_buffer *ab, void *va) | |
1232 | @@ -98,6 +130,23 @@ void audit_net_cb(struct audit_buffer *ab, void *va) | |
1233 | net_mask_names, NET_PERMS_MASK); | |
1234 | } | |
1235 | } | |
1236 | + if (sa->u.net->family == AF_UNIX) { | |
1237 | + if ((aad(sa)->request & ~NET_PEER_MASK) && aad(sa)->net.addr) | |
1238 | + audit_unix_addr(ab, "addr", | |
1239 | + unix_addr(aad(sa)->net.addr), | |
1240 | + aad(sa)->net.addrlen); | |
1241 | + else | |
1242 | + audit_unix_sk_addr(ab, "addr", sa->u.net->sk); | |
1243 | + if (aad(sa)->request & NET_PEER_MASK) { | |
1244 | + if (aad(sa)->net.addr) | |
1245 | + audit_unix_addr(ab, "peer_addr", | |
1246 | + unix_addr(aad(sa)->net.addr), | |
1247 | + aad(sa)->net.addrlen); | |
1248 | + else | |
1249 | + audit_unix_sk_addr(ab, "peer_addr", | |
1250 | + aad(sa)->net.peer_sk); | |
1251 | + } | |
1252 | + } | |
1253 | if (aad(sa)->peer) { | |
1254 | audit_log_format(ab, " peer="); | |
1255 | aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, | |
1256 | @@ -172,6 +221,127 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk) | |
1257 | return error; | |
1258 | } | |
1259 | ||
1260 | +#define af_select(FAMILY, FN, DEF_FN) \ | |
1261 | +({ \ | |
1262 | + int __e; \ | |
1263 | + switch ((FAMILY)) { \ | |
1264 | + case AF_UNIX: \ | |
1265 | + __e = aa_unix_ ## FN; \ | |
1266 | + break; \ | |
1267 | + default: \ | |
1268 | + __e = DEF_FN; \ | |
1269 | + } \ | |
1270 | + __e; \ | |
1271 | +}) | |
1272 | + | |
1273 | +/* TODO: push into lsm.c ???? */ | |
1274 | + | |
1275 | +/* revaliation, get/set attr, shutdown */ | |
1276 | +int aa_sock_perm(const char *op, u32 request, struct socket *sock) | |
1277 | +{ | |
1278 | + AA_BUG(!sock); | |
1279 | + AA_BUG(!sock->sk); | |
1280 | + AA_BUG(in_interrupt()); | |
1281 | + | |
1282 | + return af_select(sock->sk->sk_family, | |
1283 | + sock_perm(op, request, sock), | |
1284 | + aa_sk_perm(op, request, sock->sk)); | |
1285 | +} | |
1286 | + | |
1287 | +int aa_sock_create_perm(struct aa_label *label, int family, int type, | |
1288 | + int protocol) | |
1289 | +{ | |
1290 | + AA_BUG(!label); | |
1291 | + /* TODO: .... */ | |
1292 | + AA_BUG(in_interrupt()); | |
1293 | + | |
1294 | + return af_select(family, | |
1295 | + create_perm(label, family, type, protocol), | |
1296 | + aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, | |
1297 | + type, protocol)); | |
1298 | +} | |
1299 | + | |
1300 | +int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, | |
1301 | + int addrlen) | |
1302 | +{ | |
1303 | + AA_BUG(!sock); | |
1304 | + AA_BUG(!sock->sk); | |
1305 | + AA_BUG(!address); | |
1306 | + /* TODO: .... */ | |
1307 | + AA_BUG(in_interrupt()); | |
1308 | + | |
1309 | + return af_select(sock->sk->sk_family, | |
1310 | + bind_perm(sock, address, addrlen), | |
1311 | + aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk)); | |
1312 | +} | |
1313 | + | |
1314 | +int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, | |
1315 | + int addrlen) | |
1316 | +{ | |
1317 | + AA_BUG(!sock); | |
1318 | + AA_BUG(!sock->sk); | |
1319 | + AA_BUG(!address); | |
1320 | + /* TODO: .... */ | |
1321 | + AA_BUG(in_interrupt()); | |
1322 | + | |
1323 | + return af_select(sock->sk->sk_family, | |
1324 | + connect_perm(sock, address, addrlen), | |
1325 | + aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk)); | |
1326 | +} | |
1327 | + | |
1328 | +int aa_sock_listen_perm(struct socket *sock, int backlog) | |
1329 | +{ | |
1330 | + AA_BUG(!sock); | |
1331 | + AA_BUG(!sock->sk); | |
1332 | + /* TODO: .... */ | |
1333 | + AA_BUG(in_interrupt()); | |
1334 | + | |
1335 | + return af_select(sock->sk->sk_family, | |
1336 | + listen_perm(sock, backlog), | |
1337 | + aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk)); | |
1338 | +} | |
1339 | + | |
1340 | +/* ability of sock to connect, not peer address binding */ | |
1341 | +int aa_sock_accept_perm(struct socket *sock, struct socket *newsock) | |
1342 | +{ | |
1343 | + AA_BUG(!sock); | |
1344 | + AA_BUG(!sock->sk); | |
1345 | + AA_BUG(!newsock); | |
1346 | + /* TODO: .... */ | |
1347 | + AA_BUG(in_interrupt()); | |
1348 | + | |
1349 | + return af_select(sock->sk->sk_family, | |
1350 | + accept_perm(sock, newsock), | |
1351 | + aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk)); | |
1352 | +} | |
1353 | + | |
1354 | +/* sendmsg, recvmsg */ | |
1355 | +int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, | |
1356 | + struct msghdr *msg, int size) | |
1357 | +{ | |
1358 | + AA_BUG(!sock); | |
1359 | + AA_BUG(!sock->sk); | |
1360 | + AA_BUG(!msg); | |
1361 | + /* TODO: .... */ | |
1362 | + AA_BUG(in_interrupt()); | |
1363 | + | |
1364 | + return af_select(sock->sk->sk_family, | |
1365 | + msg_perm(op, request, sock, msg, size), | |
1366 | + aa_sk_perm(op, request, sock->sk)); | |
1367 | +} | |
1368 | + | |
1369 | +/* revaliation, get/set attr, opt */ | |
1370 | +int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, | |
1371 | + int optname) | |
1372 | +{ | |
1373 | + AA_BUG(!sock); | |
1374 | + AA_BUG(!sock->sk); | |
1375 | + AA_BUG(in_interrupt()); | |
1376 | + | |
1377 | + return af_select(sock->sk->sk_family, | |
1378 | + opt_perm(op, request, sock, level, optname), | |
1379 | + aa_sk_perm(op, request, sock->sk)); | |
1380 | +} | |
1381 | ||
1382 | int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, | |
1383 | struct socket *sock) | |
1384 | @@ -180,5 +350,7 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, | |
1385 | AA_BUG(!sock); | |
1386 | AA_BUG(!sock->sk); | |
1387 | ||
1388 | - return aa_label_sk_perm(label, op, request, sock->sk); | |
1389 | + return af_select(sock->sk->sk_family, | |
1390 | + file_perm(label, op, request, sock), | |
1391 | + aa_label_sk_perm(label, op, request, sock->sk)); | |
1392 | } | |
1393 | -- | |
d75b40d3 | 1394 | 2.14.1 |
daaa955e | 1395 |