]>
Commit | Line | Data |
---|---|---|
840c5ba4 | 1 | commit 97b3200925ba627346432edf521d49de8bb018a3 |
0776672e AM |
2 | Author: John Johansen <john.johansen@canonical.com> |
3 | Date: Mon Oct 4 15:03:36 2010 -0700 | |
4 | ||
5 | UBUNTU: SAUCE: AppArmor: basic networking rules | |
6 | ||
7 | Base support for network mediation. | |
8 | ||
9 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
5882c9d4 | 10 | |
5882c9d4 | 11 | diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore |
840c5ba4 | 12 | index 9cdec70d72b8..d5b291e94264 100644 |
5882c9d4 AM |
13 | --- a/security/apparmor/.gitignore |
14 | +++ b/security/apparmor/.gitignore | |
15 | @@ -1,5 +1,6 @@ | |
16 | # | |
17 | # Generated include files | |
18 | # | |
19 | +net_names.h | |
20 | capability_names.h | |
21 | rlim_names.h | |
fc63ffa9 | 22 | diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile |
840c5ba4 | 23 | index ad369a7aac24..a7dc10be232d 100644 |
fc63ffa9 AM |
24 | --- a/security/apparmor/Makefile |
25 | +++ b/security/apparmor/Makefile | |
5882c9d4 | 26 | @@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o |
ceaf2cfb | 27 | |
fc63ffa9 AM |
28 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
29 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | |
840c5ba4 AM |
30 | - resource.o secid.o file.o policy_ns.o |
31 | + resource.o secid.o file.o policy_ns.o net.o | |
5882c9d4 | 32 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o |
ceaf2cfb | 33 | |
948a1326 | 34 | -clean-files := capability_names.h rlim_names.h |
1e8b8f9b | 35 | +clean-files := capability_names.h rlim_names.h net_names.h |
ceaf2cfb | 36 | |
ceaf2cfb | 37 | |
948a1326 | 38 | # Build a lower case string table of capability names |
5882c9d4 AM |
39 | @@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ |
40 | -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ | |
41 | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ | |
948a1326 | 42 | |
1e8b8f9b | 43 | +# Build a lower case string table of address family names |
948a1326 | 44 | +# Transform lines from |
1e8b8f9b AM |
45 | +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ |
46 | +# #define AF_INET 2 /* Internet IP Protocol */ | |
47 | +# to | |
48 | +# [1] = "local", | |
49 | +# [2] = "inet", | |
50 | +# | |
51 | +# and build the securityfs entries for the mapping. | |
52 | +# Transforms lines from | |
53 | +# #define AF_INET 2 /* Internet IP Protocol */ | |
948a1326 | 54 | +# to |
1e8b8f9b | 55 | +# #define AA_FS_AF_MASK "local inet" |
ceaf2cfb | 56 | +quiet_cmd_make-af = GEN $@ |
948a1326 | 57 | +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ |
1e8b8f9b AM |
58 | + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ |
59 | + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ | |
60 | + echo "};" >> $@ ;\ | |
61 | + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ | |
62 | + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ | |
63 | + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ | |
64 | + | |
65 | +# Build a lower case string table of sock type names | |
66 | +# Transform lines from | |
67 | +# SOCK_STREAM = 1, | |
68 | +# to | |
69 | +# [1] = "stream", | |
70 | +quiet_cmd_make-sock = GEN $@ | |
71 | +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ | |
72 | + sed $^ >>$@ -r -n \ | |
73 | + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ | |
948a1326 | 74 | + echo "};" >> $@ |
1e8b8f9b AM |
75 | |
76 | # Build a lower case string table of rlimit names. | |
77 | # Transforms lines from | |
5882c9d4 | 78 | @@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ |
1e8b8f9b AM |
79 | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ |
80 | ||
fc63ffa9 | 81 | $(obj)/capability.o : $(obj)/capability_names.h |
1e8b8f9b | 82 | +$(obj)/net.o : $(obj)/net_names.h |
fc63ffa9 | 83 | $(obj)/resource.o : $(obj)/rlim_names.h |
5882c9d4 | 84 | $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ |
1e8b8f9b | 85 | $(src)/Makefile |
5882c9d4 AM |
86 | @@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ |
87 | $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ | |
1e8b8f9b | 88 | $(src)/Makefile |
948a1326 | 89 | $(call cmd,make-rlim) |
1e8b8f9b AM |
90 | +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ |
91 | + $(srctree)/include/linux/net.h \ | |
92 | + $(src)/Makefile | |
948a1326 | 93 | + $(call cmd,make-af) |
1e8b8f9b AM |
94 | + $(call cmd,make-sock) |
95 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c | |
840c5ba4 | 96 | index 41073f70eb41..4d236736cfb8 100644 |
1e8b8f9b AM |
97 | --- a/security/apparmor/apparmorfs.c |
98 | +++ b/security/apparmor/apparmorfs.c | |
840c5ba4 | 99 | @@ -1209,6 +1209,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { |
5882c9d4 | 100 | AA_FS_DIR("policy", aa_fs_entry_policy), |
1e8b8f9b AM |
101 | AA_FS_DIR("domain", aa_fs_entry_domain), |
102 | AA_FS_DIR("file", aa_fs_entry_file), | |
103 | + AA_FS_DIR("network", aa_fs_entry_network), | |
104 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | |
105 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), | |
5882c9d4 | 106 | AA_FS_DIR("caps", aa_fs_entry_caps), |
1e8b8f9b | 107 | diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h |
840c5ba4 | 108 | index fdc4774318ba..0df708e8748b 100644 |
1e8b8f9b AM |
109 | --- a/security/apparmor/include/audit.h |
110 | +++ b/security/apparmor/include/audit.h | |
840c5ba4 AM |
111 | @@ -127,6 +127,10 @@ struct apparmor_audit_data { |
112 | int rlim; | |
113 | unsigned long max; | |
114 | } rlim; | |
1e8b8f9b AM |
115 | + struct { |
116 | + int type, protocol; | |
117 | + struct sock *sk; | |
118 | + } net; | |
119 | }; | |
120 | }; | |
121 | ||
fc63ffa9 | 122 | diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h |
ceaf2cfb | 123 | new file mode 100644 |
840c5ba4 | 124 | index 000000000000..55da1dad8720 |
ceaf2cfb | 125 | --- /dev/null |
fc63ffa9 | 126 | +++ b/security/apparmor/include/net.h |
840c5ba4 | 127 | @@ -0,0 +1,59 @@ |
9474138d AM |
128 | +/* |
129 | + * AppArmor security module | |
130 | + * | |
fc63ffa9 | 131 | + * This file contains AppArmor network mediation definitions. |
9474138d AM |
132 | + * |
133 | + * Copyright (C) 1998-2008 Novell/SUSE | |
1e8b8f9b | 134 | + * Copyright 2009-2012 Canonical Ltd. |
9474138d AM |
135 | + * |
136 | + * This program is free software; you can redistribute it and/or | |
137 | + * modify it under the terms of the GNU General Public License as | |
138 | + * published by the Free Software Foundation, version 2 of the | |
139 | + * License. | |
140 | + */ | |
141 | + | |
fc63ffa9 AM |
142 | +#ifndef __AA_NET_H |
143 | +#define __AA_NET_H | |
2380c486 | 144 | + |
fc63ffa9 | 145 | +#include <net/sock.h> |
9474138d | 146 | + |
1e8b8f9b AM |
147 | +#include "apparmorfs.h" |
148 | + | |
fc63ffa9 AM |
149 | +/* struct aa_net - network confinement data |
150 | + * @allowed: basic network families permissions | |
151 | + * @audit_network: which network permissions to force audit | |
152 | + * @quiet_network: which network permissions to quiet rejects | |
153 | + */ | |
154 | +struct aa_net { | |
155 | + u16 allow[AF_MAX]; | |
156 | + u16 audit[AF_MAX]; | |
157 | + u16 quiet[AF_MAX]; | |
158 | +}; | |
ceaf2cfb | 159 | + |
1e8b8f9b AM |
160 | +extern struct aa_fs_entry aa_fs_entry_network[]; |
161 | + | |
840c5ba4 AM |
162 | +#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \ |
163 | + struct lsm_network_audit NAME ## _net = { .sk = (SK), \ | |
164 | + .family = (F)}; \ | |
165 | + DEFINE_AUDIT_DATA(NAME, \ | |
166 | + ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \ | |
167 | + LSM_AUDIT_DATA_NONE, \ | |
168 | + OP); \ | |
169 | + NAME.u.net = &(NAME ## _net); \ | |
170 | + aad(&NAME)->net.type = (T); \ | |
171 | + aad(&NAME)->net.protocol = (P) | |
172 | + | |
173 | +#define DEFINE_AUDIT_SK(NAME, OP, SK) \ | |
174 | + DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ | |
175 | + (SK)->sk_protocol) | |
176 | + | |
177 | +extern int aa_net_perm(const char *op, struct aa_profile *profile, u16 family, | |
fc63ffa9 | 178 | + int type, int protocol, struct sock *sk); |
840c5ba4 | 179 | +extern int aa_revalidate_sk(const char *op, struct sock *sk); |
ceaf2cfb | 180 | + |
fc63ffa9 AM |
181 | +static inline void aa_free_net_rules(struct aa_net *new) |
182 | +{ | |
183 | + /* NOP */ | |
ceaf2cfb AM |
184 | +} |
185 | + | |
fc63ffa9 AM |
186 | +#endif /* __AA_NET_H */ |
187 | diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h | |
840c5ba4 | 188 | index 67bc96afe541..a3d18ea8d730 100644 |
fc63ffa9 AM |
189 | --- a/security/apparmor/include/policy.h |
190 | +++ b/security/apparmor/include/policy.h | |
840c5ba4 | 191 | @@ -28,6 +28,7 @@ |
fc63ffa9 AM |
192 | #include "capability.h" |
193 | #include "domain.h" | |
194 | #include "file.h" | |
195 | +#include "net.h" | |
840c5ba4 | 196 | #include "lib.h" |
fc63ffa9 AM |
197 | #include "resource.h" |
198 | ||
840c5ba4 | 199 | @@ -132,6 +133,7 @@ struct aa_data { |
1e8b8f9b | 200 | * @policy: general match rules governing policy |
fc63ffa9 AM |
201 | * @file: The set of rules governing basic file access and domain transitions |
202 | * @caps: capabilities for the profile | |
203 | + * @net: network controls for the profile | |
204 | * @rlimits: rlimits for the profile | |
205 | * | |
5882c9d4 | 206 | * @dents: dentries for the profiles file entries in apparmorfs |
840c5ba4 | 207 | @@ -174,6 +176,7 @@ struct aa_profile { |
1e8b8f9b | 208 | struct aa_policydb policy; |
fc63ffa9 AM |
209 | struct aa_file_rules file; |
210 | struct aa_caps caps; | |
211 | + struct aa_net net; | |
212 | struct aa_rlimit rlimits; | |
fc63ffa9 | 213 | |
840c5ba4 | 214 | struct aa_loaddata *rawdata; |
fc63ffa9 | 215 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c |
840c5ba4 | 216 | index 709eacd23909..e3017129a404 100644 |
fc63ffa9 AM |
217 | --- a/security/apparmor/lsm.c |
218 | +++ b/security/apparmor/lsm.c | |
840c5ba4 | 219 | @@ -33,6 +33,7 @@ |
fc63ffa9 AM |
220 | #include "include/context.h" |
221 | #include "include/file.h" | |
222 | #include "include/ipc.h" | |
223 | +#include "include/net.h" | |
224 | #include "include/path.h" | |
225 | #include "include/policy.h" | |
840c5ba4 AM |
226 | #include "include/policy_ns.h" |
227 | @@ -587,6 +588,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, | |
fc63ffa9 AM |
228 | return error; |
229 | } | |
230 | ||
231 | +static int apparmor_socket_create(int family, int type, int protocol, int kern) | |
ceaf2cfb | 232 | +{ |
fc63ffa9 AM |
233 | + struct aa_profile *profile; |
234 | + int error = 0; | |
ceaf2cfb | 235 | + |
fc63ffa9 AM |
236 | + if (kern) |
237 | + return 0; | |
ceaf2cfb | 238 | + |
fc63ffa9 AM |
239 | + profile = __aa_current_profile(); |
240 | + if (!unconfined(profile)) | |
241 | + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, | |
242 | + NULL); | |
243 | + return error; | |
244 | +} | |
ceaf2cfb | 245 | + |
fc63ffa9 AM |
246 | +static int apparmor_socket_bind(struct socket *sock, |
247 | + struct sockaddr *address, int addrlen) | |
9474138d | 248 | +{ |
fc63ffa9 | 249 | + struct sock *sk = sock->sk; |
9474138d | 250 | + |
fc63ffa9 AM |
251 | + return aa_revalidate_sk(OP_BIND, sk); |
252 | +} | |
ceaf2cfb | 253 | + |
fc63ffa9 AM |
254 | +static int apparmor_socket_connect(struct socket *sock, |
255 | + struct sockaddr *address, int addrlen) | |
256 | +{ | |
257 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 258 | + |
fc63ffa9 | 259 | + return aa_revalidate_sk(OP_CONNECT, sk); |
9474138d AM |
260 | +} |
261 | + | |
fc63ffa9 | 262 | +static int apparmor_socket_listen(struct socket *sock, int backlog) |
9474138d | 263 | +{ |
fc63ffa9 AM |
264 | + struct sock *sk = sock->sk; |
265 | + | |
266 | + return aa_revalidate_sk(OP_LISTEN, sk); | |
9474138d AM |
267 | +} |
268 | + | |
fc63ffa9 | 269 | +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) |
9474138d | 270 | +{ |
fc63ffa9 | 271 | + struct sock *sk = sock->sk; |
9474138d | 272 | + |
fc63ffa9 AM |
273 | + return aa_revalidate_sk(OP_ACCEPT, sk); |
274 | +} | |
9474138d | 275 | + |
fc63ffa9 AM |
276 | +static int apparmor_socket_sendmsg(struct socket *sock, |
277 | + struct msghdr *msg, int size) | |
76514441 | 278 | +{ |
fc63ffa9 | 279 | + struct sock *sk = sock->sk; |
76514441 | 280 | + |
fc63ffa9 | 281 | + return aa_revalidate_sk(OP_SENDMSG, sk); |
76514441 AM |
282 | +} |
283 | + | |
fc63ffa9 AM |
284 | +static int apparmor_socket_recvmsg(struct socket *sock, |
285 | + struct msghdr *msg, int size, int flags) | |
286 | +{ | |
287 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 288 | + |
fc63ffa9 AM |
289 | + return aa_revalidate_sk(OP_RECVMSG, sk); |
290 | +} | |
ceaf2cfb | 291 | + |
fc63ffa9 AM |
292 | +static int apparmor_socket_getsockname(struct socket *sock) |
293 | +{ | |
294 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 295 | + |
fc63ffa9 | 296 | + return aa_revalidate_sk(OP_GETSOCKNAME, sk); |
9474138d AM |
297 | +} |
298 | + | |
fc63ffa9 | 299 | +static int apparmor_socket_getpeername(struct socket *sock) |
9474138d | 300 | +{ |
fc63ffa9 | 301 | + struct sock *sk = sock->sk; |
9474138d | 302 | + |
fc63ffa9 AM |
303 | + return aa_revalidate_sk(OP_GETPEERNAME, sk); |
304 | +} | |
ceaf2cfb | 305 | + |
fc63ffa9 AM |
306 | +static int apparmor_socket_getsockopt(struct socket *sock, int level, |
307 | + int optname) | |
308 | +{ | |
309 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 310 | + |
fc63ffa9 AM |
311 | + return aa_revalidate_sk(OP_GETSOCKOPT, sk); |
312 | +} | |
ceaf2cfb | 313 | + |
fc63ffa9 AM |
314 | +static int apparmor_socket_setsockopt(struct socket *sock, int level, |
315 | + int optname) | |
316 | +{ | |
317 | + struct sock *sk = sock->sk; | |
2380c486 | 318 | + |
fc63ffa9 AM |
319 | + return aa_revalidate_sk(OP_SETSOCKOPT, sk); |
320 | +} | |
ceaf2cfb | 321 | + |
fc63ffa9 AM |
322 | +static int apparmor_socket_shutdown(struct socket *sock, int how) |
323 | +{ | |
324 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 325 | + |
840c5ba4 | 326 | + return aa_revalidate_sk(OP_SHUTDOWN, sk); |
ceaf2cfb | 327 | +} |
948a1326 | 328 | + |
c2c0f25c AM |
329 | static struct security_hook_list apparmor_hooks[] = { |
330 | LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), | |
331 | LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), | |
840c5ba4 | 332 | @@ -616,6 +715,19 @@ static struct security_hook_list apparmor_hooks[] = { |
c2c0f25c AM |
333 | LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), |
334 | LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), | |
5d26c04f | 335 | |
c2c0f25c AM |
336 | + LSM_HOOK_INIT(socket_create, apparmor_socket_create), |
337 | + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), | |
338 | + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), | |
339 | + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), | |
340 | + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), | |
341 | + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), | |
342 | + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), | |
343 | + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), | |
344 | + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), | |
345 | + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), | |
346 | + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), | |
347 | + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), | |
348 | + | |
349 | LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), | |
350 | LSM_HOOK_INIT(cred_free, apparmor_cred_free), | |
351 | LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), | |
fc63ffa9 | 352 | diff --git a/security/apparmor/net.c b/security/apparmor/net.c |
ceaf2cfb | 353 | new file mode 100644 |
840c5ba4 | 354 | index 000000000000..b9c8cd0e882e |
ceaf2cfb | 355 | --- /dev/null |
fc63ffa9 | 356 | +++ b/security/apparmor/net.c |
840c5ba4 | 357 | @@ -0,0 +1,148 @@ |
2380c486 | 358 | +/* |
9474138d AM |
359 | + * AppArmor security module |
360 | + * | |
fc63ffa9 | 361 | + * This file contains AppArmor network mediation |
2380c486 | 362 | + * |
9474138d | 363 | + * Copyright (C) 1998-2008 Novell/SUSE |
1e8b8f9b | 364 | + * Copyright 2009-2012 Canonical Ltd. |
2380c486 | 365 | + * |
9474138d AM |
366 | + * This program is free software; you can redistribute it and/or |
367 | + * modify it under the terms of the GNU General Public License as | |
368 | + * published by the Free Software Foundation, version 2 of the | |
369 | + * License. | |
2380c486 JR |
370 | + */ |
371 | + | |
9474138d AM |
372 | +#include "include/apparmor.h" |
373 | +#include "include/audit.h" | |
374 | +#include "include/context.h" | |
fc63ffa9 | 375 | +#include "include/net.h" |
9474138d | 376 | +#include "include/policy.h" |
2380c486 | 377 | + |
1e8b8f9b AM |
378 | +#include "net_names.h" |
379 | + | |
380 | +struct aa_fs_entry aa_fs_entry_network[] = { | |
381 | + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), | |
382 | + { } | |
9474138d AM |
383 | +}; |
384 | + | |
fc63ffa9 | 385 | +/* audit callback for net specific fields */ |
76514441 | 386 | +static void audit_cb(struct audit_buffer *ab, void *va) |
9474138d | 387 | +{ |
76514441 | 388 | + struct common_audit_data *sa = va; |
9474138d | 389 | + |
fc63ffa9 | 390 | + audit_log_format(ab, " family="); |
1e8b8f9b AM |
391 | + if (address_family_names[sa->u.net->family]) { |
392 | + audit_log_string(ab, address_family_names[sa->u.net->family]); | |
fc63ffa9 | 393 | + } else { |
1e8b8f9b | 394 | + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); |
fc63ffa9 | 395 | + } |
fc63ffa9 | 396 | + audit_log_format(ab, " sock_type="); |
840c5ba4 AM |
397 | + if (sock_type_names[aad(sa)->net.type]) { |
398 | + audit_log_string(ab, sock_type_names[aad(sa)->net.type]); | |
fc63ffa9 | 399 | + } else { |
840c5ba4 | 400 | + audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type); |
fc63ffa9 | 401 | + } |
840c5ba4 | 402 | + audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol); |
2380c486 JR |
403 | +} |
404 | + | |
405 | +/** | |
fc63ffa9 AM |
406 | + * audit_net - audit network access |
407 | + * @profile: profile being enforced (NOT NULL) | |
408 | + * @op: operation being checked | |
409 | + * @family: network family | |
410 | + * @type: network type | |
411 | + * @protocol: network protocol | |
412 | + * @sk: socket auditing is being applied to | |
413 | + * @error: error code for failure else 0 | |
ceaf2cfb | 414 | + * |
fc63ffa9 | 415 | + * Returns: %0 or sa->error else other errorcode on failure |
2380c486 | 416 | + */ |
840c5ba4 AM |
417 | +static int audit_net(struct aa_profile *profile, const char *op, u16 family, |
418 | + int type, int protocol, struct sock *sk, int error) | |
2380c486 | 419 | +{ |
fc63ffa9 | 420 | + int audit_type = AUDIT_APPARMOR_AUTO; |
840c5ba4 AM |
421 | + DEFINE_AUDIT_NET(sa, op, sk, family, type, protocol); |
422 | + | |
423 | + aad(&sa)->error = error; | |
424 | + | |
425 | + if (likely(!aad(&sa)->error)) { | |
1e8b8f9b | 426 | + u16 audit_mask = profile->net.audit[sa.u.net->family]; |
fc63ffa9 | 427 | + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && |
840c5ba4 | 428 | + !(1 << aad(&sa)->net.type & audit_mask))) |
ceaf2cfb | 429 | + return 0; |
fc63ffa9 AM |
430 | + audit_type = AUDIT_APPARMOR_AUDIT; |
431 | + } else { | |
1e8b8f9b | 432 | + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; |
fc63ffa9 | 433 | + u16 kill_mask = 0; |
840c5ba4 | 434 | + u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask; |
9474138d | 435 | + |
fc63ffa9 AM |
436 | + if (denied & kill_mask) |
437 | + audit_type = AUDIT_APPARMOR_KILL; | |
9474138d | 438 | + |
fc63ffa9 AM |
439 | + if ((denied & quiet_mask) && |
440 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && | |
441 | + AUDIT_MODE(profile) != AUDIT_ALL) | |
840c5ba4 | 442 | + return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error; |
9474138d AM |
443 | + } |
444 | + | |
840c5ba4 | 445 | + return aa_audit(audit_type, profile, &sa, audit_cb); |
ceaf2cfb AM |
446 | +} |
447 | + | |
2380c486 | 448 | +/** |
fc63ffa9 AM |
449 | + * aa_net_perm - very course network access check |
450 | + * @op: operation being checked | |
451 | + * @profile: profile being enforced (NOT NULL) | |
452 | + * @family: network family | |
453 | + * @type: network type | |
454 | + * @protocol: network protocol | |
2380c486 | 455 | + * |
fc63ffa9 | 456 | + * Returns: %0 else error if permission denied |
2380c486 | 457 | + */ |
840c5ba4 AM |
458 | +int aa_net_perm(const char *op, struct aa_profile *profile, u16 family, |
459 | + int type, int protocol, struct sock *sk) | |
2380c486 | 460 | +{ |
fc63ffa9 AM |
461 | + u16 family_mask; |
462 | + int error; | |
ceaf2cfb | 463 | + |
fc63ffa9 AM |
464 | + if ((family < 0) || (family >= AF_MAX)) |
465 | + return -EINVAL; | |
ceaf2cfb | 466 | + |
fc63ffa9 AM |
467 | + if ((type < 0) || (type >= SOCK_MAX)) |
468 | + return -EINVAL; | |
76514441 | 469 | + |
fc63ffa9 AM |
470 | + /* unix domain and netlink sockets are handled by ipc */ |
471 | + if (family == AF_UNIX || family == AF_NETLINK) | |
472 | + return 0; | |
76514441 | 473 | + |
fc63ffa9 | 474 | + family_mask = profile->net.allow[family]; |
2380c486 | 475 | + |
fc63ffa9 | 476 | + error = (family_mask & (1 << type)) ? 0 : -EACCES; |
ceaf2cfb | 477 | + |
fc63ffa9 | 478 | + return audit_net(profile, op, family, type, protocol, sk, error); |
2380c486 JR |
479 | +} |
480 | + | |
76514441 | 481 | +/** |
fc63ffa9 AM |
482 | + * aa_revalidate_sk - Revalidate access to a sock |
483 | + * @op: operation being checked | |
484 | + * @sk: sock being revalidated (NOT NULL) | |
76514441 | 485 | + * |
fc63ffa9 | 486 | + * Returns: %0 else error if permission denied |
76514441 | 487 | + */ |
840c5ba4 | 488 | +int aa_revalidate_sk(const char *op, struct sock *sk) |
2380c486 | 489 | +{ |
fc63ffa9 AM |
490 | + struct aa_profile *profile; |
491 | + int error = 0; | |
2380c486 | 492 | + |
fc63ffa9 AM |
493 | + /* aa_revalidate_sk should not be called from interrupt context |
494 | + * don't mediate these calls as they are not task related | |
495 | + */ | |
496 | + if (in_interrupt()) | |
497 | + return 0; | |
ceaf2cfb | 498 | + |
fc63ffa9 AM |
499 | + profile = __aa_current_profile(); |
500 | + if (!unconfined(profile)) | |
501 | + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, | |
502 | + sk->sk_protocol, sk); | |
2380c486 | 503 | + |
fc63ffa9 | 504 | + return error; |
2380c486 | 505 | +} |
fc63ffa9 | 506 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c |
840c5ba4 | 507 | index def1fbd6bdfd..9fe7b9d4500f 100644 |
fc63ffa9 AM |
508 | --- a/security/apparmor/policy.c |
509 | +++ b/security/apparmor/policy.c | |
840c5ba4 | 510 | @@ -237,6 +237,7 @@ void aa_free_profile(struct aa_profile *profile) |
fc63ffa9 AM |
511 | |
512 | aa_free_file_rules(&profile->file); | |
513 | aa_free_cap_rules(&profile->caps); | |
514 | + aa_free_net_rules(&profile->net); | |
515 | aa_free_rlimit_rules(&profile->rlimits); | |
516 | ||
5882c9d4 | 517 | kzfree(profile->dirname); |
fc63ffa9 | 518 | diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c |
840c5ba4 | 519 | index 2e37c9c26bbd..bc23a5b3b113 100644 |
fc63ffa9 AM |
520 | --- a/security/apparmor/policy_unpack.c |
521 | +++ b/security/apparmor/policy_unpack.c | |
840c5ba4 | 522 | @@ -217,6 +217,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) |
fc63ffa9 AM |
523 | return 0; |
524 | } | |
525 | ||
526 | +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) | |
2380c486 | 527 | +{ |
fc63ffa9 AM |
528 | + if (unpack_nameX(e, AA_U16, name)) { |
529 | + if (!inbounds(e, sizeof(u16))) | |
530 | + return 0; | |
531 | + if (data) | |
532 | + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); | |
533 | + e->pos += sizeof(u16); | |
534 | + return 1; | |
2380c486 | 535 | + } |
2380c486 JR |
536 | + return 0; |
537 | +} | |
538 | + | |
fc63ffa9 AM |
539 | static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) |
540 | { | |
541 | if (unpack_nameX(e, AA_U32, name)) { | |
840c5ba4 | 542 | @@ -519,7 +532,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) |
fc63ffa9 AM |
543 | { |
544 | struct aa_profile *profile = NULL; | |
840c5ba4 AM |
545 | const char *tmpname, *tmpns = NULL, *name = NULL; |
546 | - size_t ns_len; | |
547 | + size_t ns_len, size = 0; | |
548 | struct rhashtable_params params = { 0 }; | |
549 | char *key = NULL; | |
550 | struct aa_data *data; | |
551 | @@ -635,6 +648,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |
fc63ffa9 AM |
552 | if (!unpack_rlimits(e, profile)) |
553 | goto fail; | |
554 | ||
ceaf2cfb | 555 | + size = unpack_array(e, "net_allowed_af"); |
2380c486 | 556 | + if (size) { |
2380c486 JR |
557 | + |
558 | + for (i = 0; i < size; i++) { | |
fc63ffa9 AM |
559 | + /* discard extraneous rules that this kernel will |
560 | + * never request | |
561 | + */ | |
1efb82ae | 562 | + if (i >= AF_MAX) { |
fc63ffa9 AM |
563 | + u16 tmp; |
564 | + if (!unpack_u16(e, &tmp, NULL) || | |
565 | + !unpack_u16(e, &tmp, NULL) || | |
566 | + !unpack_u16(e, &tmp, NULL)) | |
567 | + goto fail; | |
568 | + continue; | |
569 | + } | |
76514441 | 570 | + if (!unpack_u16(e, &profile->net.allow[i], NULL)) |
2380c486 | 571 | + goto fail; |
ceaf2cfb | 572 | + if (!unpack_u16(e, &profile->net.audit[i], NULL)) |
2380c486 | 573 | + goto fail; |
ceaf2cfb | 574 | + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) |
2380c486 JR |
575 | + goto fail; |
576 | + } | |
ceaf2cfb | 577 | + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) |
2380c486 | 578 | + goto fail; |
2380c486 | 579 | + } |
1e8b8f9b AM |
580 | + /* |
581 | + * allow unix domain and netlink sockets they are handled | |
582 | + * by IPC | |
583 | + */ | |
76514441 AM |
584 | + profile->net.allow[AF_UNIX] = 0xffff; |
585 | + profile->net.allow[AF_NETLINK] = 0xffff; | |
2380c486 | 586 | + |
1e8b8f9b AM |
587 | if (unpack_nameX(e, AA_STRUCT, "policydb")) { |
588 | /* generic policy dfa - optional and may be NULL */ | |
589 | profile->policy.dfa = unpack_dfa(e); | |
840c5ba4 | 590 | commit b866a43c2897f5469c9d787426144074a3713f6a |
0776672e AM |
591 | Author: John Johansen <john.johansen@canonical.com> |
592 | Date: Fri Jun 29 17:34:00 2012 -0700 | |
5882c9d4 | 593 | |
0776672e AM |
594 | apparmor: Fix quieting of audit messages for network mediation |
595 | ||
596 | If a profile specified a quieting of network denials for a given rule by | |
597 | either the quiet or deny rule qualifiers, the resultant quiet mask for | |
598 | denied requests was applied incorrectly, resulting in two potential bugs. | |
599 | 1. The misapplied quiet mask would prevent denials from being correctly | |
600 | tested against the kill mask/mode. Thus network access requests that | |
601 | should have resulted in the application being killed did not. | |
602 | ||
603 | 2. The actual quieting of the denied network request was not being applied. | |
604 | This would result in network rejections always being logged even when | |
605 | they had been specifically marked as quieted. | |
606 | ||
607 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
5882c9d4 AM |
608 | |
609 | diff --git a/security/apparmor/net.c b/security/apparmor/net.c | |
840c5ba4 | 610 | index b9c8cd0e882e..5ba19ad1d65c 100644 |
5882c9d4 AM |
611 | --- a/security/apparmor/net.c |
612 | +++ b/security/apparmor/net.c | |
840c5ba4 | 613 | @@ -74,7 +74,7 @@ static int audit_net(struct aa_profile *profile, const char *op, u16 family, |
5882c9d4 AM |
614 | } else { |
615 | u16 quiet_mask = profile->net.quiet[sa.u.net->family]; | |
616 | u16 kill_mask = 0; | |
840c5ba4 AM |
617 | - u16 denied = (1 << aad(&sa)->net.type) & ~quiet_mask; |
618 | + u16 denied = (1 << aad(&sa)->net.type); | |
5882c9d4 AM |
619 | |
620 | if (denied & kill_mask) | |
621 | audit_type = AUDIT_APPARMOR_KILL; | |
840c5ba4 | 622 | commit 4429c3f9522b608300cfe1ae148dc6cdadf3d76c |
0776672e AM |
623 | Author: John Johansen <john.johansen@canonical.com> |
624 | Date: Wed May 16 10:58:05 2012 -0700 | |
1e8b8f9b | 625 | |
0776672e AM |
626 | UBUNTU: SAUCE: apparmor: Add the ability to mediate mount |
627 | ||
628 | Add the ability for apparmor to do mediation of mount operations. Mount | |
629 | rules require an updated apparmor_parser (2.8 series) for policy compilation. | |
630 | ||
631 | The basic form of the rules are. | |
632 | ||
633 | [audit] [deny] mount [conds]* [device] [ -> [conds] path], | |
634 | [audit] [deny] remount [conds]* [path], | |
635 | [audit] [deny] umount [conds]* [path], | |
636 | [audit] [deny] pivotroot [oldroot=<value>] <path> | |
637 | ||
638 | remount is just a short cut for mount options=remount | |
639 | ||
640 | where [conds] can be | |
641 | fstype=<expr> | |
642 | options=<expr> | |
643 | ||
644 | Example mount commands | |
645 | mount, # allow all mounts, but not umount or pivotroot | |
646 | ||
647 | mount fstype=procfs, # allow mounting procfs anywhere | |
648 | ||
649 | mount options=(bind, ro) /foo -> /bar, # readonly bind mount | |
650 | ||
651 | mount /dev/sda -> /mnt, | |
652 | ||
653 | mount /dev/sd** -> /mnt/**, | |
654 | ||
655 | mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ | |
656 | ||
657 | umount, | |
658 | ||
659 | umount /m*, | |
660 | ||
661 | See the apparmor userspace for full documentation | |
662 | ||
663 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
664 | Acked-by: Kees Cook <kees@ubuntu.com> | |
5882c9d4 | 665 | |
fc63ffa9 | 666 | diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile |
840c5ba4 | 667 | index a7dc10be232d..01368441f230 100644 |
fc63ffa9 AM |
668 | --- a/security/apparmor/Makefile |
669 | +++ b/security/apparmor/Makefile | |
1e8b8f9b AM |
670 | @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o |
671 | ||
948a1326 | 672 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
fc63ffa9 | 673 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ |
840c5ba4 AM |
674 | - resource.o secid.o file.o policy_ns.o net.o |
675 | + resource.o secid.o file.o policy_ns.o net.o mount.o | |
5882c9d4 | 676 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o |
fc63ffa9 | 677 | |
1e8b8f9b | 678 | clean-files := capability_names.h rlim_names.h net_names.h |
1e8b8f9b | 679 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c |
840c5ba4 | 680 | index 4d236736cfb8..2e8d09e2368b 100644 |
1e8b8f9b AM |
681 | --- a/security/apparmor/apparmorfs.c |
682 | +++ b/security/apparmor/apparmorfs.c | |
840c5ba4 AM |
683 | @@ -1205,11 +1205,24 @@ static struct aa_fs_entry aa_fs_entry_policy[] = { |
684 | { } | |
685 | }; | |
1e8b8f9b AM |
686 | |
687 | +static struct aa_fs_entry aa_fs_entry_mount[] = { | |
688 | + AA_FS_FILE_STRING("mask", "mount umount"), | |
689 | + { } | |
690 | +}; | |
691 | + | |
692 | +static struct aa_fs_entry aa_fs_entry_namespaces[] = { | |
693 | + AA_FS_FILE_BOOLEAN("profile", 1), | |
694 | + AA_FS_FILE_BOOLEAN("pivot_root", 1), | |
695 | + { } | |
840c5ba4 AM |
696 | +}; |
697 | + | |
1e8b8f9b | 698 | static struct aa_fs_entry aa_fs_entry_features[] = { |
840c5ba4 | 699 | AA_FS_DIR("policy", aa_fs_entry_policy), |
1e8b8f9b AM |
700 | AA_FS_DIR("domain", aa_fs_entry_domain), |
701 | AA_FS_DIR("file", aa_fs_entry_file), | |
702 | AA_FS_DIR("network", aa_fs_entry_network), | |
703 | + AA_FS_DIR("mount", aa_fs_entry_mount), | |
704 | + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), | |
705 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | |
706 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), | |
5882c9d4 | 707 | AA_FS_DIR("caps", aa_fs_entry_caps), |
1e8b8f9b | 708 | diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c |
840c5ba4 | 709 | index 001e133a3c8c..708b7e22b9b5 100644 |
1e8b8f9b AM |
710 | --- a/security/apparmor/domain.c |
711 | +++ b/security/apparmor/domain.c | |
840c5ba4 | 712 | @@ -237,7 +237,7 @@ static const char *next_name(int xtype, const char *name) |
1e8b8f9b AM |
713 | * |
714 | * Returns: refcounted profile, or NULL on failure (MAYBE NULL) | |
715 | */ | |
716 | -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |
717 | +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |
718 | { | |
719 | struct aa_profile *new_profile = NULL; | |
840c5ba4 | 720 | struct aa_ns *ns = profile->ns; |
1e8b8f9b | 721 | diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h |
840c5ba4 | 722 | index 1750cc0721c1..3383dc66f30f 100644 |
1e8b8f9b AM |
723 | --- a/security/apparmor/include/apparmor.h |
724 | +++ b/security/apparmor/include/apparmor.h | |
840c5ba4 | 725 | @@ -27,8 +27,9 @@ |
1e8b8f9b AM |
726 | #define AA_CLASS_NET 4 |
727 | #define AA_CLASS_RLIMITS 5 | |
728 | #define AA_CLASS_DOMAIN 6 | |
729 | +#define AA_CLASS_MOUNT 7 | |
730 | ||
731 | -#define AA_CLASS_LAST AA_CLASS_DOMAIN | |
732 | +#define AA_CLASS_LAST AA_CLASS_MOUNT | |
733 | ||
734 | /* Control parameters settable through module/boot flags */ | |
735 | extern enum audit_mode aa_g_audit; | |
736 | diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h | |
840c5ba4 | 737 | index 0df708e8748b..41374ad89547 100644 |
1e8b8f9b AM |
738 | --- a/security/apparmor/include/audit.h |
739 | +++ b/security/apparmor/include/audit.h | |
840c5ba4 AM |
740 | @@ -70,6 +70,10 @@ enum audit_type { |
741 | #define OP_FMMAP "file_mmap" | |
742 | #define OP_FMPROT "file_mprotect" | |
1e8b8f9b | 743 | |
840c5ba4 AM |
744 | +#define OP_PIVOTROOT "pivotroot" |
745 | +#define OP_MOUNT "mount" | |
746 | +#define OP_UMOUNT "umount" | |
747 | + | |
748 | #define OP_CREATE "create" | |
749 | #define OP_POST_CREATE "post_create" | |
750 | #define OP_BIND "bind" | |
751 | @@ -127,6 +131,13 @@ struct apparmor_audit_data { | |
752 | int rlim; | |
1e8b8f9b AM |
753 | unsigned long max; |
754 | } rlim; | |
840c5ba4 | 755 | + struct { |
1e8b8f9b AM |
756 | + const char *src_name; |
757 | + const char *type; | |
758 | + const char *trans; | |
759 | + const char *data; | |
760 | + unsigned long flags; | |
761 | + } mnt; | |
840c5ba4 AM |
762 | struct { |
763 | int type, protocol; | |
764 | struct sock *sk; | |
1e8b8f9b | 765 | diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h |
840c5ba4 | 766 | index 30544729878a..7bd21d20a2bd 100644 |
1e8b8f9b AM |
767 | --- a/security/apparmor/include/domain.h |
768 | +++ b/security/apparmor/include/domain.h | |
769 | @@ -23,6 +23,8 @@ struct aa_domain { | |
770 | char **table; | |
771 | }; | |
772 | ||
773 | +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); | |
774 | + | |
775 | int apparmor_bprm_set_creds(struct linux_binprm *bprm); | |
776 | int apparmor_bprm_secureexec(struct linux_binprm *bprm); | |
777 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm); | |
778 | diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h | |
ceaf2cfb | 779 | new file mode 100644 |
840c5ba4 | 780 | index 000000000000..a43b1d62e428 |
ceaf2cfb | 781 | --- /dev/null |
1e8b8f9b AM |
782 | +++ b/security/apparmor/include/mount.h |
783 | @@ -0,0 +1,54 @@ | |
2380c486 | 784 | +/* |
9474138d AM |
785 | + * AppArmor security module |
786 | + * | |
1e8b8f9b | 787 | + * This file contains AppArmor file mediation function definitions. |
2380c486 | 788 | + * |
1e8b8f9b | 789 | + * Copyright 2012 Canonical Ltd. |
2380c486 | 790 | + * |
9474138d AM |
791 | + * This program is free software; you can redistribute it and/or |
792 | + * modify it under the terms of the GNU General Public License as | |
793 | + * published by the Free Software Foundation, version 2 of the | |
794 | + * License. | |
2380c486 JR |
795 | + */ |
796 | + | |
1e8b8f9b AM |
797 | +#ifndef __AA_MOUNT_H |
798 | +#define __AA_MOUNT_H | |
fc63ffa9 | 799 | + |
1e8b8f9b AM |
800 | +#include <linux/fs.h> |
801 | +#include <linux/path.h> | |
2380c486 | 802 | + |
1e8b8f9b AM |
803 | +#include "domain.h" |
804 | +#include "policy.h" | |
76514441 | 805 | + |
1e8b8f9b AM |
806 | +/* mount perms */ |
807 | +#define AA_MAY_PIVOTROOT 0x01 | |
808 | +#define AA_MAY_MOUNT 0x02 | |
809 | +#define AA_MAY_UMOUNT 0x04 | |
810 | +#define AA_AUDIT_DATA 0x40 | |
811 | +#define AA_CONT_MATCH 0x40 | |
fc63ffa9 | 812 | + |
1e8b8f9b | 813 | +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) |
fc63ffa9 | 814 | + |
0776672e | 815 | +int aa_remount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b | 816 | + unsigned long flags, void *data); |
fc63ffa9 | 817 | + |
0776672e | 818 | +int aa_bind_mount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b | 819 | + const char *old_name, unsigned long flags); |
fc63ffa9 | 820 | + |
fc63ffa9 | 821 | + |
0776672e | 822 | +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, |
1e8b8f9b | 823 | + unsigned long flags); |
fc63ffa9 | 824 | + |
0776672e | 825 | +int aa_move_mount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b AM |
826 | + const char *old_name); |
827 | + | |
828 | +int aa_new_mount(struct aa_profile *profile, const char *dev_name, | |
0776672e | 829 | + const struct path *path, const char *type, unsigned long flags, |
1e8b8f9b AM |
830 | + void *data); |
831 | + | |
832 | +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); | |
833 | + | |
0776672e AM |
834 | +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, |
835 | + const struct path *new_path); | |
1e8b8f9b AM |
836 | + |
837 | +#endif /* __AA_MOUNT_H */ | |
838 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c | |
840c5ba4 | 839 | index e3017129a404..ee58a2cca74f 100644 |
1e8b8f9b AM |
840 | --- a/security/apparmor/lsm.c |
841 | +++ b/security/apparmor/lsm.c | |
840c5ba4 | 842 | @@ -38,6 +38,7 @@ |
1e8b8f9b | 843 | #include "include/policy.h" |
840c5ba4 | 844 | #include "include/policy_ns.h" |
1e8b8f9b AM |
845 | #include "include/procattr.h" |
846 | +#include "include/mount.h" | |
847 | ||
848 | /* Flag indicating whether initialization completed */ | |
849 | int apparmor_initialized __initdata; | |
840c5ba4 | 850 | @@ -479,6 +480,61 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, |
1e8b8f9b AM |
851 | !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); |
852 | } | |
853 | ||
0776672e AM |
854 | +static int apparmor_sb_mount(const char *dev_name, const struct path *path, |
855 | + const char *type, unsigned long flags, void *data) | |
2380c486 | 856 | +{ |
1e8b8f9b AM |
857 | + struct aa_profile *profile; |
858 | + int error = 0; | |
76514441 | 859 | + |
1e8b8f9b AM |
860 | + /* Discard magic */ |
861 | + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | |
862 | + flags &= ~MS_MGC_MSK; | |
2380c486 | 863 | + |
1e8b8f9b AM |
864 | + flags &= ~AA_MS_IGNORE_MASK; |
865 | + | |
866 | + profile = __aa_current_profile(); | |
867 | + if (!unconfined(profile)) { | |
868 | + if (flags & MS_REMOUNT) | |
869 | + error = aa_remount(profile, path, flags, data); | |
870 | + else if (flags & MS_BIND) | |
871 | + error = aa_bind_mount(profile, path, dev_name, flags); | |
872 | + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | | |
873 | + MS_UNBINDABLE)) | |
874 | + error = aa_mount_change_type(profile, path, flags); | |
875 | + else if (flags & MS_MOVE) | |
876 | + error = aa_move_mount(profile, path, dev_name); | |
877 | + else | |
878 | + error = aa_new_mount(profile, dev_name, path, type, | |
879 | + flags, data); | |
2380c486 | 880 | + } |
1e8b8f9b AM |
881 | + return error; |
882 | +} | |
2380c486 | 883 | + |
1e8b8f9b AM |
884 | +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) |
885 | +{ | |
886 | + struct aa_profile *profile; | |
887 | + int error = 0; | |
888 | + | |
889 | + profile = __aa_current_profile(); | |
890 | + if (!unconfined(profile)) | |
891 | + error = aa_umount(profile, mnt, flags); | |
892 | + | |
893 | + return error; | |
2380c486 JR |
894 | +} |
895 | + | |
0776672e AM |
896 | +static int apparmor_sb_pivotroot(const struct path *old_path, |
897 | + const struct path *new_path) | |
2380c486 | 898 | +{ |
1e8b8f9b AM |
899 | + struct aa_profile *profile; |
900 | + int error = 0; | |
901 | + | |
902 | + profile = __aa_current_profile(); | |
903 | + if (!unconfined(profile)) | |
904 | + error = aa_pivotroot(profile, old_path, new_path); | |
905 | + | |
906 | + return error; | |
2380c486 JR |
907 | +} |
908 | + | |
1e8b8f9b AM |
909 | static int apparmor_getprocattr(struct task_struct *task, char *name, |
910 | char **value) | |
911 | { | |
840c5ba4 | 912 | @@ -692,6 +748,10 @@ static struct security_hook_list apparmor_hooks[] = { |
c2c0f25c AM |
913 | LSM_HOOK_INIT(capget, apparmor_capget), |
914 | LSM_HOOK_INIT(capable, apparmor_capable), | |
1e8b8f9b | 915 | |
c2c0f25c AM |
916 | + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), |
917 | + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), | |
918 | + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), | |
840c5ba4 | 919 | + |
c2c0f25c AM |
920 | LSM_HOOK_INIT(path_link, apparmor_path_link), |
921 | LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), | |
922 | LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), | |
1e8b8f9b AM |
923 | diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c |
924 | new file mode 100644 | |
840c5ba4 | 925 | index 000000000000..9e95a41c015c |
1e8b8f9b AM |
926 | --- /dev/null |
927 | +++ b/security/apparmor/mount.c | |
840c5ba4 | 928 | @@ -0,0 +1,616 @@ |
1e8b8f9b AM |
929 | +/* |
930 | + * AppArmor security module | |
fc63ffa9 | 931 | + * |
1e8b8f9b | 932 | + * This file contains AppArmor mediation of files |
ceaf2cfb | 933 | + * |
1e8b8f9b AM |
934 | + * Copyright (C) 1998-2008 Novell/SUSE |
935 | + * Copyright 2009-2012 Canonical Ltd. | |
936 | + * | |
937 | + * This program is free software; you can redistribute it and/or | |
938 | + * modify it under the terms of the GNU General Public License as | |
939 | + * published by the Free Software Foundation, version 2 of the | |
940 | + * License. | |
ceaf2cfb | 941 | + */ |
2380c486 | 942 | + |
1e8b8f9b AM |
943 | +#include <linux/fs.h> |
944 | +#include <linux/mount.h> | |
945 | +#include <linux/namei.h> | |
2380c486 | 946 | + |
1e8b8f9b AM |
947 | +#include "include/apparmor.h" |
948 | +#include "include/audit.h" | |
949 | +#include "include/context.h" | |
950 | +#include "include/domain.h" | |
951 | +#include "include/file.h" | |
952 | +#include "include/match.h" | |
953 | +#include "include/mount.h" | |
954 | +#include "include/path.h" | |
955 | +#include "include/policy.h" | |
ceaf2cfb | 956 | + |
2380c486 | 957 | + |
1e8b8f9b AM |
958 | +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) |
959 | +{ | |
960 | + if (flags & MS_RDONLY) | |
961 | + audit_log_format(ab, "ro"); | |
962 | + else | |
963 | + audit_log_format(ab, "rw"); | |
964 | + if (flags & MS_NOSUID) | |
965 | + audit_log_format(ab, ", nosuid"); | |
966 | + if (flags & MS_NODEV) | |
967 | + audit_log_format(ab, ", nodev"); | |
968 | + if (flags & MS_NOEXEC) | |
969 | + audit_log_format(ab, ", noexec"); | |
970 | + if (flags & MS_SYNCHRONOUS) | |
971 | + audit_log_format(ab, ", sync"); | |
972 | + if (flags & MS_REMOUNT) | |
973 | + audit_log_format(ab, ", remount"); | |
974 | + if (flags & MS_MANDLOCK) | |
975 | + audit_log_format(ab, ", mand"); | |
976 | + if (flags & MS_DIRSYNC) | |
977 | + audit_log_format(ab, ", dirsync"); | |
978 | + if (flags & MS_NOATIME) | |
979 | + audit_log_format(ab, ", noatime"); | |
980 | + if (flags & MS_NODIRATIME) | |
981 | + audit_log_format(ab, ", nodiratime"); | |
982 | + if (flags & MS_BIND) | |
983 | + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); | |
984 | + if (flags & MS_MOVE) | |
985 | + audit_log_format(ab, ", move"); | |
986 | + if (flags & MS_SILENT) | |
987 | + audit_log_format(ab, ", silent"); | |
988 | + if (flags & MS_POSIXACL) | |
989 | + audit_log_format(ab, ", acl"); | |
990 | + if (flags & MS_UNBINDABLE) | |
991 | + audit_log_format(ab, flags & MS_REC ? ", runbindable" : | |
992 | + ", unbindable"); | |
993 | + if (flags & MS_PRIVATE) | |
994 | + audit_log_format(ab, flags & MS_REC ? ", rprivate" : | |
995 | + ", private"); | |
996 | + if (flags & MS_SLAVE) | |
997 | + audit_log_format(ab, flags & MS_REC ? ", rslave" : | |
998 | + ", slave"); | |
999 | + if (flags & MS_SHARED) | |
1000 | + audit_log_format(ab, flags & MS_REC ? ", rshared" : | |
1001 | + ", shared"); | |
1002 | + if (flags & MS_RELATIME) | |
1003 | + audit_log_format(ab, ", relatime"); | |
1004 | + if (flags & MS_I_VERSION) | |
1005 | + audit_log_format(ab, ", iversion"); | |
1006 | + if (flags & MS_STRICTATIME) | |
1007 | + audit_log_format(ab, ", strictatime"); | |
1008 | + if (flags & MS_NOUSER) | |
1009 | + audit_log_format(ab, ", nouser"); | |
2380c486 JR |
1010 | +} |
1011 | + | |
ceaf2cfb | 1012 | +/** |
1e8b8f9b AM |
1013 | + * audit_cb - call back for mount specific audit fields |
1014 | + * @ab: audit_buffer (NOT NULL) | |
1015 | + * @va: audit struct to audit values of (NOT NULL) | |
ceaf2cfb | 1016 | + */ |
1e8b8f9b | 1017 | +static void audit_cb(struct audit_buffer *ab, void *va) |
2380c486 | 1018 | +{ |
1e8b8f9b | 1019 | + struct common_audit_data *sa = va; |
9474138d | 1020 | + |
840c5ba4 | 1021 | + if (aad(sa)->mnt.type) { |
1e8b8f9b | 1022 | + audit_log_format(ab, " fstype="); |
840c5ba4 | 1023 | + audit_log_untrustedstring(ab, aad(sa)->mnt.type); |
1e8b8f9b | 1024 | + } |
840c5ba4 | 1025 | + if (aad(sa)->mnt.src_name) { |
1e8b8f9b | 1026 | + audit_log_format(ab, " srcname="); |
840c5ba4 | 1027 | + audit_log_untrustedstring(ab, aad(sa)->mnt.src_name); |
1e8b8f9b | 1028 | + } |
840c5ba4 | 1029 | + if (aad(sa)->mnt.trans) { |
1e8b8f9b | 1030 | + audit_log_format(ab, " trans="); |
840c5ba4 | 1031 | + audit_log_untrustedstring(ab, aad(sa)->mnt.trans); |
1e8b8f9b | 1032 | + } |
840c5ba4 | 1033 | + if (aad(sa)->mnt.flags) { |
1e8b8f9b | 1034 | + audit_log_format(ab, " flags=\""); |
840c5ba4 | 1035 | + audit_mnt_flags(ab, aad(sa)->mnt.flags); |
1e8b8f9b AM |
1036 | + audit_log_format(ab, "\""); |
1037 | + } | |
840c5ba4 | 1038 | + if (aad(sa)->mnt.data) { |
1e8b8f9b | 1039 | + audit_log_format(ab, " options="); |
840c5ba4 | 1040 | + audit_log_untrustedstring(ab, aad(sa)->mnt.data); |
1e8b8f9b | 1041 | + } |
9474138d | 1042 | +} |
2380c486 | 1043 | + |
fc63ffa9 | 1044 | +/** |
1e8b8f9b AM |
1045 | + * audit_mount - handle the auditing of mount operations |
1046 | + * @profile: the profile being enforced (NOT NULL) | |
1047 | + * @gfp: allocation flags | |
1048 | + * @op: operation being mediated (NOT NULL) | |
1049 | + * @name: name of object being mediated (MAYBE NULL) | |
1050 | + * @src_name: src_name of object being mediated (MAYBE_NULL) | |
1051 | + * @type: type of filesystem (MAYBE_NULL) | |
1052 | + * @trans: name of trans (MAYBE NULL) | |
1053 | + * @flags: filesystem idependent mount flags | |
1054 | + * @data: filesystem mount flags | |
1055 | + * @request: permissions requested | |
1056 | + * @perms: the permissions computed for the request (NOT NULL) | |
1057 | + * @info: extra information message (MAYBE NULL) | |
1058 | + * @error: 0 if operation allowed else failure error code | |
9474138d | 1059 | + * |
1e8b8f9b | 1060 | + * Returns: %0 or error on failure |
76514441 | 1061 | + */ |
840c5ba4 | 1062 | +static int audit_mount(struct aa_profile *profile, gfp_t gfp, const char *op, |
1e8b8f9b AM |
1063 | + const char *name, const char *src_name, |
1064 | + const char *type, const char *trans, | |
1065 | + unsigned long flags, const void *data, u32 request, | |
1066 | + struct file_perms *perms, const char *info, int error) | |
2380c486 | 1067 | +{ |
1e8b8f9b | 1068 | + int audit_type = AUDIT_APPARMOR_AUTO; |
840c5ba4 | 1069 | + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); |
9474138d | 1070 | + |
1e8b8f9b AM |
1071 | + if (likely(!error)) { |
1072 | + u32 mask = perms->audit; | |
9474138d | 1073 | + |
1e8b8f9b AM |
1074 | + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) |
1075 | + mask = 0xffff; | |
76514441 | 1076 | + |
1e8b8f9b AM |
1077 | + /* mask off perms that are not being force audited */ |
1078 | + request &= mask; | |
fc63ffa9 | 1079 | + |
1e8b8f9b AM |
1080 | + if (likely(!request)) |
1081 | + return 0; | |
1082 | + audit_type = AUDIT_APPARMOR_AUDIT; | |
1083 | + } else { | |
1084 | + /* only report permissions that were denied */ | |
1085 | + request = request & ~perms->allow; | |
1086 | + | |
1087 | + if (request & perms->kill) | |
1088 | + audit_type = AUDIT_APPARMOR_KILL; | |
1089 | + | |
1090 | + /* quiet known rejects, assumes quiet and kill do not overlap */ | |
1091 | + if ((request & perms->quiet) && | |
1092 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && | |
1093 | + AUDIT_MODE(profile) != AUDIT_ALL) | |
1094 | + request &= ~perms->quiet; | |
1095 | + | |
1096 | + if (!request) | |
1097 | + return COMPLAIN_MODE(profile) ? | |
1098 | + complain_error(error) : error; | |
1099 | + } | |
1100 | + | |
840c5ba4 AM |
1101 | + aad(&sa)->name = name; |
1102 | + aad(&sa)->mnt.src_name = src_name; | |
1103 | + aad(&sa)->mnt.type = type; | |
1104 | + aad(&sa)->mnt.trans = trans; | |
1105 | + aad(&sa)->mnt.flags = flags; | |
1e8b8f9b | 1106 | + if (data && (perms->audit & AA_AUDIT_DATA)) |
840c5ba4 AM |
1107 | + aad(&sa)->mnt.data = data; |
1108 | + aad(&sa)->info = info; | |
1109 | + aad(&sa)->error = error; | |
1e8b8f9b | 1110 | + |
840c5ba4 | 1111 | + return aa_audit(audit_type, profile, &sa, audit_cb); |
76514441 AM |
1112 | +} |
1113 | + | |
1114 | +/** | |
1e8b8f9b AM |
1115 | + * match_mnt_flags - Do an ordered match on mount flags |
1116 | + * @dfa: dfa to match against | |
1117 | + * @state: state to start in | |
1118 | + * @flags: mount flags to match against | |
76514441 | 1119 | + * |
1e8b8f9b AM |
1120 | + * Mount flags are encoded as an ordered match. This is done instead of |
1121 | + * checking against a simple bitmask, to allow for logical operations | |
1122 | + * on the flags. | |
76514441 | 1123 | + * |
1e8b8f9b | 1124 | + * Returns: next state after flags match |
76514441 | 1125 | + */ |
1e8b8f9b AM |
1126 | +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, |
1127 | + unsigned long flags) | |
9474138d | 1128 | +{ |
1e8b8f9b | 1129 | + unsigned int i; |
fc63ffa9 | 1130 | + |
1e8b8f9b AM |
1131 | + for (i = 0; i <= 31 ; ++i) { |
1132 | + if ((1 << i) & flags) | |
1133 | + state = aa_dfa_next(dfa, state, i + 1); | |
1134 | + } | |
1135 | + | |
1136 | + return state; | |
9474138d AM |
1137 | +} |
1138 | + | |
1139 | +/** | |
1e8b8f9b AM |
1140 | + * compute_mnt_perms - compute mount permission associated with @state |
1141 | + * @dfa: dfa to match against (NOT NULL) | |
1142 | + * @state: state match finished in | |
ceaf2cfb | 1143 | + * |
1e8b8f9b | 1144 | + * Returns: mount permissions |
9474138d | 1145 | + */ |
1e8b8f9b AM |
1146 | +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, |
1147 | + unsigned int state) | |
9474138d | 1148 | +{ |
1e8b8f9b | 1149 | + struct file_perms perms; |
2380c486 | 1150 | + |
1e8b8f9b AM |
1151 | + perms.kill = 0; |
1152 | + perms.allow = dfa_user_allow(dfa, state); | |
1153 | + perms.audit = dfa_user_audit(dfa, state); | |
1154 | + perms.quiet = dfa_user_quiet(dfa, state); | |
1155 | + perms.xindex = dfa_user_xindex(dfa, state); | |
1156 | + | |
1157 | + return perms; | |
1158 | +} | |
1159 | + | |
840c5ba4 | 1160 | +static const char *mnt_info_table[] = { |
1e8b8f9b AM |
1161 | + "match succeeded", |
1162 | + "failed mntpnt match", | |
1163 | + "failed srcname match", | |
1164 | + "failed type match", | |
1165 | + "failed flags match", | |
1166 | + "failed data match" | |
1167 | +}; | |
1168 | + | |
1169 | +/* | |
1170 | + * Returns 0 on success else element that match failed in, this is the | |
1171 | + * index into the mnt_info_table above | |
1172 | + */ | |
1173 | +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, | |
1174 | + const char *mntpnt, const char *devname, | |
1175 | + const char *type, unsigned long flags, | |
1176 | + void *data, bool binary, struct file_perms *perms) | |
1177 | +{ | |
1178 | + unsigned int state; | |
1179 | + | |
1180 | + state = aa_dfa_match(dfa, start, mntpnt); | |
1181 | + state = aa_dfa_null_transition(dfa, state); | |
1182 | + if (!state) | |
1183 | + return 1; | |
1184 | + | |
1185 | + if (devname) | |
1186 | + state = aa_dfa_match(dfa, state, devname); | |
1187 | + state = aa_dfa_null_transition(dfa, state); | |
1188 | + if (!state) | |
1189 | + return 2; | |
1190 | + | |
1191 | + if (type) | |
1192 | + state = aa_dfa_match(dfa, state, type); | |
1193 | + state = aa_dfa_null_transition(dfa, state); | |
1194 | + if (!state) | |
1195 | + return 3; | |
1196 | + | |
1197 | + state = match_mnt_flags(dfa, state, flags); | |
1198 | + if (!state) | |
1199 | + return 4; | |
1200 | + *perms = compute_mnt_perms(dfa, state); | |
1201 | + if (perms->allow & AA_MAY_MOUNT) | |
1202 | + return 0; | |
1203 | + | |
1204 | + /* only match data if not binary and the DFA flags data is expected */ | |
1205 | + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { | |
1206 | + state = aa_dfa_null_transition(dfa, state); | |
1207 | + if (!state) | |
1208 | + return 4; | |
1209 | + | |
1210 | + state = aa_dfa_match(dfa, state, data); | |
1211 | + if (!state) | |
1212 | + return 5; | |
1213 | + *perms = compute_mnt_perms(dfa, state); | |
1214 | + if (perms->allow & AA_MAY_MOUNT) | |
1215 | + return 0; | |
fc63ffa9 | 1216 | + } |
1e8b8f9b AM |
1217 | + |
1218 | + /* failed at end of flags match */ | |
1219 | + return 4; | |
9474138d AM |
1220 | +} |
1221 | + | |
ceaf2cfb | 1222 | +/** |
1e8b8f9b AM |
1223 | + * match_mnt - handle path matching for mount |
1224 | + * @profile: the confining profile | |
1225 | + * @mntpnt: string for the mntpnt (NOT NULL) | |
1226 | + * @devname: string for the devname/src_name (MAYBE NULL) | |
1227 | + * @type: string for the dev type (MAYBE NULL) | |
1228 | + * @flags: mount flags to match | |
1229 | + * @data: fs mount data (MAYBE NULL) | |
1230 | + * @binary: whether @data is binary | |
1231 | + * @perms: Returns: permission found by the match | |
1232 | + * @info: Returns: infomation string about the match for logging | |
fc63ffa9 | 1233 | + * |
1e8b8f9b | 1234 | + * Returns: 0 on success else error |
ceaf2cfb | 1235 | + */ |
1e8b8f9b AM |
1236 | +static int match_mnt(struct aa_profile *profile, const char *mntpnt, |
1237 | + const char *devname, const char *type, | |
1238 | + unsigned long flags, void *data, bool binary, | |
1239 | + struct file_perms *perms, const char **info) | |
9474138d | 1240 | +{ |
1e8b8f9b | 1241 | + int pos; |
2380c486 | 1242 | + |
1e8b8f9b AM |
1243 | + if (!profile->policy.dfa) |
1244 | + return -EACCES; | |
1245 | + | |
1246 | + pos = do_match_mnt(profile->policy.dfa, | |
1247 | + profile->policy.start[AA_CLASS_MOUNT], | |
1248 | + mntpnt, devname, type, flags, data, binary, perms); | |
1249 | + if (pos) { | |
1250 | + *info = mnt_info_table[pos]; | |
1251 | + return -EACCES; | |
1252 | + } | |
2380c486 | 1253 | + |
fc63ffa9 | 1254 | + return 0; |
9474138d | 1255 | +} |
9474138d | 1256 | + |
0776672e | 1257 | +static int path_flags(struct aa_profile *profile, const struct path *path) |
1e8b8f9b AM |
1258 | +{ |
1259 | + return profile->path_flags | | |
1260 | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; | |
1261 | +} | |
9474138d | 1262 | + |
0776672e | 1263 | +int aa_remount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b | 1264 | + unsigned long flags, void *data) |
9474138d | 1265 | +{ |
1e8b8f9b AM |
1266 | + struct file_perms perms = { }; |
1267 | + const char *name, *info = NULL; | |
1268 | + char *buffer = NULL; | |
1269 | + int binary, error; | |
1270 | + | |
1271 | + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; | |
1272 | + | |
1273 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
1274 | + &info); | |
1275 | + if (error) | |
1276 | + goto audit; | |
1277 | + | |
1278 | + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, | |
1279 | + &perms, &info); | |
1280 | + | |
1281 | +audit: | |
1282 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, | |
1283 | + NULL, flags, data, AA_MAY_MOUNT, &perms, info, | |
1284 | + error); | |
1285 | + kfree(buffer); | |
1286 | + | |
1287 | + return error; | |
9474138d AM |
1288 | +} |
1289 | + | |
0776672e | 1290 | +int aa_bind_mount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b | 1291 | + const char *dev_name, unsigned long flags) |
9474138d | 1292 | +{ |
1e8b8f9b AM |
1293 | + struct file_perms perms = { }; |
1294 | + char *buffer = NULL, *old_buffer = NULL; | |
1295 | + const char *name, *old_name = NULL, *info = NULL; | |
1296 | + struct path old_path; | |
1297 | + int error; | |
1298 | + | |
1299 | + if (!dev_name || !*dev_name) | |
1300 | + return -EINVAL; | |
1301 | + | |
1302 | + flags &= MS_REC | MS_BIND; | |
1303 | + | |
1304 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
1305 | + &info); | |
1306 | + if (error) | |
1307 | + goto audit; | |
1308 | + | |
1309 | + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); | |
1310 | + if (error) | |
1311 | + goto audit; | |
1312 | + | |
1313 | + error = aa_path_name(&old_path, path_flags(profile, &old_path), | |
1314 | + &old_buffer, &old_name, &info); | |
1315 | + path_put(&old_path); | |
1316 | + if (error) | |
1317 | + goto audit; | |
1318 | + | |
1319 | + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, | |
1320 | + &perms, &info); | |
1321 | + | |
1322 | +audit: | |
1323 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, | |
1324 | + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, | |
1325 | + info, error); | |
1326 | + kfree(buffer); | |
1327 | + kfree(old_buffer); | |
1328 | + | |
1329 | + return error; | |
9474138d | 1330 | +} |
fc63ffa9 | 1331 | + |
0776672e | 1332 | +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, |
1e8b8f9b AM |
1333 | + unsigned long flags) |
1334 | +{ | |
1335 | + struct file_perms perms = { }; | |
1336 | + char *buffer = NULL; | |
1337 | + const char *name, *info = NULL; | |
1338 | + int error; | |
1339 | + | |
1340 | + /* These are the flags allowed by do_change_type() */ | |
1341 | + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | | |
1342 | + MS_UNBINDABLE); | |
1343 | + | |
1344 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
1345 | + &info); | |
fc63ffa9 | 1346 | + if (error) |
1e8b8f9b AM |
1347 | + goto audit; |
1348 | + | |
1349 | + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, | |
1350 | + &info); | |
1351 | + | |
1352 | +audit: | |
1353 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, | |
1354 | + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, | |
1355 | + error); | |
1356 | + kfree(buffer); | |
1357 | + | |
1358 | + return error; | |
1359 | +} | |
1360 | + | |
0776672e | 1361 | +int aa_move_mount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b AM |
1362 | + const char *orig_name) |
1363 | +{ | |
1364 | + struct file_perms perms = { }; | |
1365 | + char *buffer = NULL, *old_buffer = NULL; | |
1366 | + const char *name, *old_name = NULL, *info = NULL; | |
1367 | + struct path old_path; | |
1368 | + int error; | |
1369 | + | |
1370 | + if (!orig_name || !*orig_name) | |
1371 | + return -EINVAL; | |
1372 | + | |
1373 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
1374 | + &info); | |
fc63ffa9 | 1375 | + if (error) |
1e8b8f9b AM |
1376 | + goto audit; |
1377 | + | |
1378 | + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); | |
fc63ffa9 | 1379 | + if (error) |
1e8b8f9b AM |
1380 | + goto audit; |
1381 | + | |
1382 | + error = aa_path_name(&old_path, path_flags(profile, &old_path), | |
1383 | + &old_buffer, &old_name, &info); | |
1384 | + path_put(&old_path); | |
1385 | + if (error) | |
1386 | + goto audit; | |
1387 | + | |
1388 | + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, | |
1389 | + &perms, &info); | |
1390 | + | |
1391 | +audit: | |
1392 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, | |
1393 | + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, | |
1394 | + info, error); | |
1395 | + kfree(buffer); | |
1396 | + kfree(old_buffer); | |
1397 | + | |
1398 | + return error; | |
1399 | +} | |
1400 | + | |
1401 | +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, | |
0776672e | 1402 | + const struct path *path, const char *type, unsigned long flags, |
1e8b8f9b AM |
1403 | + void *data) |
1404 | +{ | |
1405 | + struct file_perms perms = { }; | |
1406 | + char *buffer = NULL, *dev_buffer = NULL; | |
1407 | + const char *name = NULL, *dev_name = NULL, *info = NULL; | |
1408 | + int binary = 1; | |
1409 | + int error; | |
1410 | + | |
1411 | + dev_name = orig_dev_name; | |
1412 | + if (type) { | |
1413 | + int requires_dev; | |
1414 | + struct file_system_type *fstype = get_fs_type(type); | |
1415 | + if (!fstype) | |
1416 | + return -ENODEV; | |
1417 | + | |
1418 | + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; | |
1419 | + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; | |
1420 | + put_filesystem(fstype); | |
1421 | + | |
1422 | + if (requires_dev) { | |
1423 | + struct path dev_path; | |
1424 | + | |
1425 | + if (!dev_name || !*dev_name) { | |
1426 | + error = -ENOENT; | |
1427 | + goto out; | |
1428 | + } | |
1429 | + | |
1430 | + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); | |
1431 | + if (error) | |
1432 | + goto audit; | |
1433 | + | |
1434 | + error = aa_path_name(&dev_path, | |
1435 | + path_flags(profile, &dev_path), | |
1436 | + &dev_buffer, &dev_name, &info); | |
1437 | + path_put(&dev_path); | |
1438 | + if (error) | |
1439 | + goto audit; | |
1440 | + } | |
1441 | + } | |
1442 | + | |
1443 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
1444 | + &info); | |
1445 | + if (error) | |
1446 | + goto audit; | |
1447 | + | |
1448 | + error = match_mnt(profile, name, dev_name, type, flags, data, binary, | |
1449 | + &perms, &info); | |
1450 | + | |
1451 | +audit: | |
1452 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, | |
1453 | + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, | |
1454 | + error); | |
1455 | + kfree(buffer); | |
1456 | + kfree(dev_buffer); | |
1457 | + | |
1458 | +out: | |
1459 | + return error; | |
1460 | + | |
1461 | +} | |
1462 | + | |
1463 | +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) | |
1464 | +{ | |
1465 | + struct file_perms perms = { }; | |
1466 | + char *buffer = NULL; | |
1467 | + const char *name, *info = NULL; | |
1468 | + int error; | |
1469 | + | |
1470 | + struct path path = { mnt, mnt->mnt_root }; | |
1471 | + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, | |
1472 | + &info); | |
1473 | + if (error) | |
1474 | + goto audit; | |
1475 | + | |
1476 | + if (!error && profile->policy.dfa) { | |
1477 | + unsigned int state; | |
1478 | + state = aa_dfa_match(profile->policy.dfa, | |
1479 | + profile->policy.start[AA_CLASS_MOUNT], | |
1480 | + name); | |
1481 | + perms = compute_mnt_perms(profile->policy.dfa, state); | |
1482 | + } | |
1483 | + | |
1484 | + if (AA_MAY_UMOUNT & ~perms.allow) | |
1485 | + error = -EACCES; | |
1486 | + | |
1487 | +audit: | |
1488 | + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, | |
1489 | + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); | |
1490 | + kfree(buffer); | |
1491 | + | |
1492 | + return error; | |
1493 | +} | |
1494 | + | |
0776672e AM |
1495 | +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, |
1496 | + const struct path *new_path) | |
1e8b8f9b AM |
1497 | +{ |
1498 | + struct file_perms perms = { }; | |
1499 | + struct aa_profile *target = NULL; | |
1500 | + char *old_buffer = NULL, *new_buffer = NULL; | |
1501 | + const char *old_name, *new_name = NULL, *info = NULL; | |
1502 | + int error; | |
1503 | + | |
1504 | + error = aa_path_name(old_path, path_flags(profile, old_path), | |
1505 | + &old_buffer, &old_name, &info); | |
1506 | + if (error) | |
1507 | + goto audit; | |
1508 | + | |
1509 | + error = aa_path_name(new_path, path_flags(profile, new_path), | |
1510 | + &new_buffer, &new_name, &info); | |
1511 | + if (error) | |
1512 | + goto audit; | |
1513 | + | |
1514 | + if (profile->policy.dfa) { | |
1515 | + unsigned int state; | |
1516 | + state = aa_dfa_match(profile->policy.dfa, | |
1517 | + profile->policy.start[AA_CLASS_MOUNT], | |
1518 | + new_name); | |
1519 | + state = aa_dfa_null_transition(profile->policy.dfa, state); | |
1520 | + state = aa_dfa_match(profile->policy.dfa, state, old_name); | |
1521 | + perms = compute_mnt_perms(profile->policy.dfa, state); | |
1522 | + } | |
1523 | + | |
1524 | + if (AA_MAY_PIVOTROOT & perms.allow) { | |
1525 | + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { | |
1526 | + target = x_table_lookup(profile, perms.xindex); | |
1527 | + if (!target) | |
1528 | + error = -ENOENT; | |
1529 | + else | |
1530 | + error = aa_replace_current_profile(target); | |
1531 | + } | |
1532 | + } else | |
1533 | + error = -EACCES; | |
1534 | + | |
1535 | +audit: | |
1536 | + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, | |
1537 | + old_name, NULL, target ? target->base.name : NULL, | |
1538 | + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); | |
1539 | + aa_put_profile(target); | |
1540 | + kfree(old_buffer); | |
1541 | + kfree(new_buffer); | |
1542 | + | |
1543 | + return error; | |
1544 | +} |