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