]>
Commit | Line | Data |
---|---|---|
daaa955e AM |
1 | From f37356d0a41499f9222f9f2b9c0147b500ae4285 Mon Sep 17 00:00:00 2001 |
2 | From: John Johansen <john.johansen@canonical.com> | |
3 | Date: Tue, 18 Jul 2017 23:04:47 -0700 | |
4 | Subject: [PATCH 07/17] apparmor: add mount mediation | |
1e8b8f9b | 5 | |
daaa955e AM |
6 | Add basic mount mediation. That allows controlling based on basic |
7 | mount parameters. It does not include special mount parameters for | |
8 | apparmor, super block labeling, or any triggers for apparmor namespace | |
9 | parameter modifications on pivot root. | |
10 | ||
11 | default userspace policy rules have the form of | |
12 | MOUNT RULE = ( MOUNT | REMOUNT | UMOUNT ) | |
13 | ||
14 | MOUNT = [ QUALIFIERS ] 'mount' [ MOUNT CONDITIONS ] [ SOURCE FILEGLOB ] | |
15 | [ '->' MOUNTPOINT FILEGLOB ] | |
16 | ||
17 | REMOUNT = [ QUALIFIERS ] 'remount' [ MOUNT CONDITIONS ] | |
18 | MOUNTPOINT FILEGLOB | |
19 | ||
20 | UMOUNT = [ QUALIFIERS ] 'umount' [ MOUNT CONDITIONS ] MOUNTPOINT FILEGLOB | |
21 | ||
22 | MOUNT CONDITIONS = [ ( 'fstype' | 'vfstype' ) ( '=' | 'in' ) | |
23 | MOUNT FSTYPE EXPRESSION ] | |
24 | [ 'options' ( '=' | 'in' ) MOUNT FLAGS EXPRESSION ] | |
25 | ||
26 | MOUNT FSTYPE EXPRESSION = ( MOUNT FSTYPE LIST | MOUNT EXPRESSION ) | |
27 | ||
28 | MOUNT FSTYPE LIST = Comma separated list of valid filesystem and | |
29 | virtual filesystem types (eg ext4, debugfs, etc) | |
30 | ||
31 | MOUNT FLAGS EXPRESSION = ( MOUNT FLAGS LIST | MOUNT EXPRESSION ) | |
32 | ||
33 | MOUNT FLAGS LIST = Comma separated list of MOUNT FLAGS. | |
34 | ||
35 | MOUNT FLAGS = ( 'ro' | 'rw' | 'nosuid' | 'suid' | 'nodev' | 'dev' | | |
36 | 'noexec' | 'exec' | 'sync' | 'async' | 'remount' | | |
37 | 'mand' | 'nomand' | 'dirsync' | 'noatime' | 'atime' | | |
38 | 'nodiratime' | 'diratime' | 'bind' | 'rbind' | 'move' | | |
39 | 'verbose' | 'silent' | 'loud' | 'acl' | 'noacl' | | |
40 | 'unbindable' | 'runbindable' | 'private' | 'rprivate' | | |
41 | 'slave' | 'rslave' | 'shared' | 'rshared' | | |
42 | 'relatime' | 'norelatime' | 'iversion' | 'noiversion' | | |
43 | 'strictatime' | 'nouser' | 'user' ) | |
44 | ||
45 | MOUNT EXPRESSION = ( ALPHANUMERIC | AARE ) ... | |
46 | ||
47 | PIVOT ROOT RULE = [ QUALIFIERS ] pivot_root [ oldroot=OLD PUT FILEGLOB ] | |
48 | [ NEW ROOT FILEGLOB ] | |
49 | ||
50 | SOURCE FILEGLOB = FILEGLOB | |
51 | ||
52 | MOUNTPOINT FILEGLOB = FILEGLOB | |
53 | ||
54 | eg. | |
55 | mount, | |
56 | mount /dev/foo, | |
57 | mount options=ro /dev/foo -> /mnt/, | |
58 | mount options in (ro,atime) /dev/foo -> /mnt/, | |
59 | mount options=ro options=atime, | |
60 | ||
61 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
62 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
63 | (cherry picked from commit fa488437d0f95b2e5db1e624341fe0d5a233f729) | |
64 | --- | |
65 | security/apparmor/Makefile | 2 +- | |
66 | security/apparmor/apparmorfs.c | 8 +- | |
67 | security/apparmor/domain.c | 4 +- | |
68 | security/apparmor/include/apparmor.h | 1 + | |
69 | security/apparmor/include/audit.h | 11 + | |
70 | security/apparmor/include/domain.h | 5 + | |
71 | security/apparmor/include/mount.h | 54 +++ | |
72 | security/apparmor/lsm.c | 64 ++++ | |
73 | security/apparmor/mount.c | 696 +++++++++++++++++++++++++++++++++++ | |
74 | 9 files changed, 841 insertions(+), 4 deletions(-) | |
75 | create mode 100644 security/apparmor/include/mount.h | |
76 | create mode 100644 security/apparmor/mount.c | |
5882c9d4 | 77 | |
fc63ffa9 | 78 | diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile |
daaa955e | 79 | index a16b195274de..81a34426d024 100644 |
fc63ffa9 AM |
80 | --- a/security/apparmor/Makefile |
81 | +++ b/security/apparmor/Makefile | |
1e8b8f9b AM |
82 | @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o |
83 | ||
948a1326 | 84 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
fc63ffa9 | 85 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ |
daaa955e AM |
86 | - resource.o secid.o file.o policy_ns.o label.o |
87 | + resource.o secid.o file.o policy_ns.o label.o mount.o | |
5882c9d4 | 88 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o |
fc63ffa9 | 89 | |
daaa955e | 90 | clean-files := capability_names.h rlim_names.h |
1e8b8f9b | 91 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c |
daaa955e | 92 | index a5f9e1aa51f7..8fa6c898c44b 100644 |
1e8b8f9b AM |
93 | --- a/security/apparmor/apparmorfs.c |
94 | +++ b/security/apparmor/apparmorfs.c | |
daaa955e | 95 | @@ -2159,9 +2159,14 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = { |
840c5ba4 AM |
96 | { } |
97 | }; | |
1e8b8f9b | 98 | |
daaa955e AM |
99 | +static struct aa_sfs_entry aa_sfs_entry_mount[] = { |
100 | + AA_SFS_FILE_STRING("mask", "mount umount pivot_root"), | |
1e8b8f9b AM |
101 | + { } |
102 | +}; | |
103 | + | |
daaa955e AM |
104 | static struct aa_sfs_entry aa_sfs_entry_ns[] = { |
105 | AA_SFS_FILE_BOOLEAN("profile", 1), | |
106 | - AA_SFS_FILE_BOOLEAN("pivot_root", 1), | |
107 | + AA_SFS_FILE_BOOLEAN("pivot_root", 0), | |
108 | { } | |
109 | }; | |
110 | ||
111 | @@ -2180,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { | |
112 | AA_SFS_DIR("policy", aa_sfs_entry_policy), | |
113 | AA_SFS_DIR("domain", aa_sfs_entry_domain), | |
114 | AA_SFS_DIR("file", aa_sfs_entry_file), | |
115 | + AA_SFS_DIR("mount", aa_sfs_entry_mount), | |
116 | AA_SFS_DIR("namespaces", aa_sfs_entry_ns), | |
117 | AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | |
118 | AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit), | |
1e8b8f9b | 119 | diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c |
daaa955e | 120 | index d0594446ae3f..ffc8c75a6785 100644 |
1e8b8f9b AM |
121 | --- a/security/apparmor/domain.c |
122 | +++ b/security/apparmor/domain.c | |
daaa955e | 123 | @@ -374,8 +374,8 @@ static const char *next_name(int xtype, const char *name) |
1e8b8f9b | 124 | * |
daaa955e | 125 | * Returns: refcounted label, or NULL on failure (MAYBE NULL) |
1e8b8f9b | 126 | */ |
daaa955e AM |
127 | -static struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, |
128 | - const char **name) | |
129 | +struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, | |
130 | + const char **name) | |
1e8b8f9b | 131 | { |
daaa955e AM |
132 | struct aa_label *label = NULL; |
133 | u32 xtype = xindex & AA_X_TYPE_MASK; | |
1e8b8f9b | 134 | diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h |
daaa955e | 135 | index 962a20a75e01..829082c35faa 100644 |
1e8b8f9b AM |
136 | --- a/security/apparmor/include/apparmor.h |
137 | +++ b/security/apparmor/include/apparmor.h | |
daaa955e | 138 | @@ -27,6 +27,7 @@ |
1e8b8f9b AM |
139 | #define AA_CLASS_NET 4 |
140 | #define AA_CLASS_RLIMITS 5 | |
141 | #define AA_CLASS_DOMAIN 6 | |
142 | +#define AA_CLASS_MOUNT 7 | |
daaa955e AM |
143 | #define AA_CLASS_PTRACE 9 |
144 | #define AA_CLASS_SIGNAL 10 | |
145 | #define AA_CLASS_LABEL 16 | |
1e8b8f9b | 146 | diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h |
daaa955e | 147 | index d9a156ae11b9..c3fe1c5ef3bc 100644 |
1e8b8f9b AM |
148 | --- a/security/apparmor/include/audit.h |
149 | +++ b/security/apparmor/include/audit.h | |
daaa955e | 150 | @@ -71,6 +71,10 @@ enum audit_type { |
840c5ba4 | 151 | #define OP_FMPROT "file_mprotect" |
daaa955e | 152 | #define OP_INHERIT "file_inherit" |
1e8b8f9b | 153 | |
840c5ba4 AM |
154 | +#define OP_PIVOTROOT "pivotroot" |
155 | +#define OP_MOUNT "mount" | |
156 | +#define OP_UMOUNT "umount" | |
157 | + | |
158 | #define OP_CREATE "create" | |
159 | #define OP_POST_CREATE "post_create" | |
160 | #define OP_BIND "bind" | |
daaa955e | 161 | @@ -132,6 +136,13 @@ struct apparmor_audit_data { |
840c5ba4 | 162 | int rlim; |
1e8b8f9b AM |
163 | unsigned long max; |
164 | } rlim; | |
daaa955e | 165 | + struct { |
1e8b8f9b AM |
166 | + const char *src_name; |
167 | + const char *type; | |
168 | + const char *trans; | |
169 | + const char *data; | |
170 | + unsigned long flags; | |
171 | + } mnt; | |
daaa955e AM |
172 | }; |
173 | }; | |
174 | ||
1e8b8f9b | 175 | diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h |
daaa955e | 176 | index bab5810b6e9a..db27403346c5 100644 |
1e8b8f9b AM |
177 | --- a/security/apparmor/include/domain.h |
178 | +++ b/security/apparmor/include/domain.h | |
daaa955e AM |
179 | @@ -15,6 +15,8 @@ |
180 | #include <linux/binfmts.h> | |
181 | #include <linux/types.h> | |
182 | ||
183 | +#include "label.h" | |
184 | + | |
185 | #ifndef __AA_DOMAIN_H | |
186 | #define __AA_DOMAIN_H | |
187 | ||
188 | @@ -29,6 +31,9 @@ struct aa_domain { | |
189 | #define AA_CHANGE_ONEXEC 4 | |
190 | #define AA_CHANGE_STACK 8 | |
1e8b8f9b | 191 | |
daaa955e AM |
192 | +struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, |
193 | + const char **name); | |
1e8b8f9b AM |
194 | + |
195 | int apparmor_bprm_set_creds(struct linux_binprm *bprm); | |
196 | int apparmor_bprm_secureexec(struct linux_binprm *bprm); | |
daaa955e | 197 | |
1e8b8f9b | 198 | diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h |
ceaf2cfb | 199 | new file mode 100644 |
daaa955e | 200 | index 000000000000..25d6067fa6ef |
ceaf2cfb | 201 | --- /dev/null |
1e8b8f9b AM |
202 | +++ b/security/apparmor/include/mount.h |
203 | @@ -0,0 +1,54 @@ | |
2380c486 | 204 | +/* |
9474138d AM |
205 | + * AppArmor security module |
206 | + * | |
1e8b8f9b | 207 | + * This file contains AppArmor file mediation function definitions. |
2380c486 | 208 | + * |
daaa955e | 209 | + * Copyright 2017 Canonical Ltd. |
2380c486 | 210 | + * |
9474138d AM |
211 | + * This program is free software; you can redistribute it and/or |
212 | + * modify it under the terms of the GNU General Public License as | |
213 | + * published by the Free Software Foundation, version 2 of the | |
214 | + * License. | |
2380c486 JR |
215 | + */ |
216 | + | |
1e8b8f9b AM |
217 | +#ifndef __AA_MOUNT_H |
218 | +#define __AA_MOUNT_H | |
fc63ffa9 | 219 | + |
1e8b8f9b AM |
220 | +#include <linux/fs.h> |
221 | +#include <linux/path.h> | |
2380c486 | 222 | + |
1e8b8f9b AM |
223 | +#include "domain.h" |
224 | +#include "policy.h" | |
76514441 | 225 | + |
1e8b8f9b AM |
226 | +/* mount perms */ |
227 | +#define AA_MAY_PIVOTROOT 0x01 | |
228 | +#define AA_MAY_MOUNT 0x02 | |
229 | +#define AA_MAY_UMOUNT 0x04 | |
230 | +#define AA_AUDIT_DATA 0x40 | |
daaa955e | 231 | +#define AA_MNT_CONT_MATCH 0x40 |
fc63ffa9 | 232 | + |
1e8b8f9b | 233 | +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) |
fc63ffa9 | 234 | + |
daaa955e | 235 | +int aa_remount(struct aa_label *label, const struct path *path, |
1e8b8f9b | 236 | + unsigned long flags, void *data); |
fc63ffa9 | 237 | + |
daaa955e | 238 | +int aa_bind_mount(struct aa_label *label, const struct path *path, |
1e8b8f9b | 239 | + const char *old_name, unsigned long flags); |
fc63ffa9 | 240 | + |
fc63ffa9 | 241 | + |
daaa955e | 242 | +int aa_mount_change_type(struct aa_label *label, const struct path *path, |
1e8b8f9b | 243 | + unsigned long flags); |
fc63ffa9 | 244 | + |
daaa955e | 245 | +int aa_move_mount(struct aa_label *label, const struct path *path, |
1e8b8f9b AM |
246 | + const char *old_name); |
247 | + | |
daaa955e | 248 | +int aa_new_mount(struct aa_label *label, const char *dev_name, |
0776672e | 249 | + const struct path *path, const char *type, unsigned long flags, |
1e8b8f9b AM |
250 | + void *data); |
251 | + | |
daaa955e | 252 | +int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags); |
1e8b8f9b | 253 | + |
daaa955e | 254 | +int aa_pivotroot(struct aa_label *label, const struct path *old_path, |
0776672e | 255 | + const struct path *new_path); |
1e8b8f9b AM |
256 | + |
257 | +#endif /* __AA_MOUNT_H */ | |
258 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c | |
daaa955e | 259 | index af22f3dfbcce..4ad0b3a45142 100644 |
1e8b8f9b AM |
260 | --- a/security/apparmor/lsm.c |
261 | +++ b/security/apparmor/lsm.c | |
840c5ba4 | 262 | @@ -38,6 +38,7 @@ |
1e8b8f9b | 263 | #include "include/policy.h" |
840c5ba4 | 264 | #include "include/policy_ns.h" |
1e8b8f9b AM |
265 | #include "include/procattr.h" |
266 | +#include "include/mount.h" | |
267 | ||
268 | /* Flag indicating whether initialization completed */ | |
daaa955e AM |
269 | int apparmor_initialized; |
270 | @@ -511,6 +512,65 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, | |
1e8b8f9b AM |
271 | !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); |
272 | } | |
273 | ||
0776672e AM |
274 | +static int apparmor_sb_mount(const char *dev_name, const struct path *path, |
275 | + const char *type, unsigned long flags, void *data) | |
2380c486 | 276 | +{ |
daaa955e | 277 | + struct aa_label *label; |
1e8b8f9b | 278 | + int error = 0; |
76514441 | 279 | + |
1e8b8f9b AM |
280 | + /* Discard magic */ |
281 | + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | |
282 | + flags &= ~MS_MGC_MSK; | |
2380c486 | 283 | + |
1e8b8f9b AM |
284 | + flags &= ~AA_MS_IGNORE_MASK; |
285 | + | |
daaa955e AM |
286 | + label = __begin_current_label_crit_section(); |
287 | + if (!unconfined(label)) { | |
1e8b8f9b | 288 | + if (flags & MS_REMOUNT) |
daaa955e | 289 | + error = aa_remount(label, path, flags, data); |
1e8b8f9b | 290 | + else if (flags & MS_BIND) |
daaa955e | 291 | + error = aa_bind_mount(label, path, dev_name, flags); |
1e8b8f9b AM |
292 | + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | |
293 | + MS_UNBINDABLE)) | |
daaa955e | 294 | + error = aa_mount_change_type(label, path, flags); |
1e8b8f9b | 295 | + else if (flags & MS_MOVE) |
daaa955e | 296 | + error = aa_move_mount(label, path, dev_name); |
1e8b8f9b | 297 | + else |
daaa955e | 298 | + error = aa_new_mount(label, dev_name, path, type, |
1e8b8f9b | 299 | + flags, data); |
2380c486 | 300 | + } |
daaa955e AM |
301 | + __end_current_label_crit_section(label); |
302 | + | |
1e8b8f9b AM |
303 | + return error; |
304 | +} | |
2380c486 | 305 | + |
1e8b8f9b AM |
306 | +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) |
307 | +{ | |
daaa955e | 308 | + struct aa_label *label; |
1e8b8f9b AM |
309 | + int error = 0; |
310 | + | |
daaa955e AM |
311 | + label = __begin_current_label_crit_section(); |
312 | + if (!unconfined(label)) | |
313 | + error = aa_umount(label, mnt, flags); | |
314 | + __end_current_label_crit_section(label); | |
1e8b8f9b AM |
315 | + |
316 | + return error; | |
2380c486 JR |
317 | +} |
318 | + | |
0776672e AM |
319 | +static int apparmor_sb_pivotroot(const struct path *old_path, |
320 | + const struct path *new_path) | |
2380c486 | 321 | +{ |
daaa955e | 322 | + struct aa_label *label; |
1e8b8f9b AM |
323 | + int error = 0; |
324 | + | |
daaa955e AM |
325 | + label = aa_get_current_label(); |
326 | + if (!unconfined(label)) | |
327 | + error = aa_pivotroot(label, old_path, new_path); | |
328 | + aa_put_label(label); | |
1e8b8f9b AM |
329 | + |
330 | + return error; | |
2380c486 JR |
331 | +} |
332 | + | |
1e8b8f9b AM |
333 | static int apparmor_getprocattr(struct task_struct *task, char *name, |
334 | char **value) | |
335 | { | |
daaa955e | 336 | @@ -682,6 +742,10 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { |
c2c0f25c AM |
337 | LSM_HOOK_INIT(capget, apparmor_capget), |
338 | LSM_HOOK_INIT(capable, apparmor_capable), | |
1e8b8f9b | 339 | |
c2c0f25c AM |
340 | + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), |
341 | + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), | |
342 | + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), | |
840c5ba4 | 343 | + |
c2c0f25c AM |
344 | LSM_HOOK_INIT(path_link, apparmor_path_link), |
345 | LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), | |
346 | LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), | |
1e8b8f9b AM |
347 | diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c |
348 | new file mode 100644 | |
daaa955e | 349 | index 000000000000..82a64b58041d |
1e8b8f9b AM |
350 | --- /dev/null |
351 | +++ b/security/apparmor/mount.c | |
daaa955e | 352 | @@ -0,0 +1,696 @@ |
1e8b8f9b AM |
353 | +/* |
354 | + * AppArmor security module | |
fc63ffa9 | 355 | + * |
1e8b8f9b | 356 | + * This file contains AppArmor mediation of files |
ceaf2cfb | 357 | + * |
1e8b8f9b | 358 | + * Copyright (C) 1998-2008 Novell/SUSE |
daaa955e | 359 | + * Copyright 2009-2017 Canonical Ltd. |
1e8b8f9b AM |
360 | + * |
361 | + * This program is free software; you can redistribute it and/or | |
362 | + * modify it under the terms of the GNU General Public License as | |
363 | + * published by the Free Software Foundation, version 2 of the | |
364 | + * License. | |
ceaf2cfb | 365 | + */ |
2380c486 | 366 | + |
1e8b8f9b AM |
367 | +#include <linux/fs.h> |
368 | +#include <linux/mount.h> | |
369 | +#include <linux/namei.h> | |
2380c486 | 370 | + |
1e8b8f9b AM |
371 | +#include "include/apparmor.h" |
372 | +#include "include/audit.h" | |
373 | +#include "include/context.h" | |
374 | +#include "include/domain.h" | |
375 | +#include "include/file.h" | |
376 | +#include "include/match.h" | |
377 | +#include "include/mount.h" | |
378 | +#include "include/path.h" | |
379 | +#include "include/policy.h" | |
ceaf2cfb | 380 | + |
2380c486 | 381 | + |
1e8b8f9b AM |
382 | +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) |
383 | +{ | |
384 | + if (flags & MS_RDONLY) | |
385 | + audit_log_format(ab, "ro"); | |
386 | + else | |
387 | + audit_log_format(ab, "rw"); | |
388 | + if (flags & MS_NOSUID) | |
389 | + audit_log_format(ab, ", nosuid"); | |
390 | + if (flags & MS_NODEV) | |
391 | + audit_log_format(ab, ", nodev"); | |
392 | + if (flags & MS_NOEXEC) | |
393 | + audit_log_format(ab, ", noexec"); | |
394 | + if (flags & MS_SYNCHRONOUS) | |
395 | + audit_log_format(ab, ", sync"); | |
396 | + if (flags & MS_REMOUNT) | |
397 | + audit_log_format(ab, ", remount"); | |
398 | + if (flags & MS_MANDLOCK) | |
399 | + audit_log_format(ab, ", mand"); | |
400 | + if (flags & MS_DIRSYNC) | |
401 | + audit_log_format(ab, ", dirsync"); | |
402 | + if (flags & MS_NOATIME) | |
403 | + audit_log_format(ab, ", noatime"); | |
404 | + if (flags & MS_NODIRATIME) | |
405 | + audit_log_format(ab, ", nodiratime"); | |
406 | + if (flags & MS_BIND) | |
407 | + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); | |
408 | + if (flags & MS_MOVE) | |
409 | + audit_log_format(ab, ", move"); | |
410 | + if (flags & MS_SILENT) | |
411 | + audit_log_format(ab, ", silent"); | |
412 | + if (flags & MS_POSIXACL) | |
413 | + audit_log_format(ab, ", acl"); | |
414 | + if (flags & MS_UNBINDABLE) | |
415 | + audit_log_format(ab, flags & MS_REC ? ", runbindable" : | |
416 | + ", unbindable"); | |
417 | + if (flags & MS_PRIVATE) | |
418 | + audit_log_format(ab, flags & MS_REC ? ", rprivate" : | |
419 | + ", private"); | |
420 | + if (flags & MS_SLAVE) | |
421 | + audit_log_format(ab, flags & MS_REC ? ", rslave" : | |
422 | + ", slave"); | |
423 | + if (flags & MS_SHARED) | |
424 | + audit_log_format(ab, flags & MS_REC ? ", rshared" : | |
425 | + ", shared"); | |
426 | + if (flags & MS_RELATIME) | |
427 | + audit_log_format(ab, ", relatime"); | |
428 | + if (flags & MS_I_VERSION) | |
429 | + audit_log_format(ab, ", iversion"); | |
430 | + if (flags & MS_STRICTATIME) | |
431 | + audit_log_format(ab, ", strictatime"); | |
432 | + if (flags & MS_NOUSER) | |
433 | + audit_log_format(ab, ", nouser"); | |
2380c486 JR |
434 | +} |
435 | + | |
ceaf2cfb | 436 | +/** |
1e8b8f9b AM |
437 | + * audit_cb - call back for mount specific audit fields |
438 | + * @ab: audit_buffer (NOT NULL) | |
439 | + * @va: audit struct to audit values of (NOT NULL) | |
ceaf2cfb | 440 | + */ |
1e8b8f9b | 441 | +static void audit_cb(struct audit_buffer *ab, void *va) |
2380c486 | 442 | +{ |
1e8b8f9b | 443 | + struct common_audit_data *sa = va; |
9474138d | 444 | + |
840c5ba4 | 445 | + if (aad(sa)->mnt.type) { |
1e8b8f9b | 446 | + audit_log_format(ab, " fstype="); |
840c5ba4 | 447 | + audit_log_untrustedstring(ab, aad(sa)->mnt.type); |
1e8b8f9b | 448 | + } |
840c5ba4 | 449 | + if (aad(sa)->mnt.src_name) { |
1e8b8f9b | 450 | + audit_log_format(ab, " srcname="); |
840c5ba4 | 451 | + audit_log_untrustedstring(ab, aad(sa)->mnt.src_name); |
1e8b8f9b | 452 | + } |
840c5ba4 | 453 | + if (aad(sa)->mnt.trans) { |
1e8b8f9b | 454 | + audit_log_format(ab, " trans="); |
840c5ba4 | 455 | + audit_log_untrustedstring(ab, aad(sa)->mnt.trans); |
1e8b8f9b | 456 | + } |
840c5ba4 | 457 | + if (aad(sa)->mnt.flags) { |
1e8b8f9b | 458 | + audit_log_format(ab, " flags=\""); |
840c5ba4 | 459 | + audit_mnt_flags(ab, aad(sa)->mnt.flags); |
1e8b8f9b AM |
460 | + audit_log_format(ab, "\""); |
461 | + } | |
840c5ba4 | 462 | + if (aad(sa)->mnt.data) { |
1e8b8f9b | 463 | + audit_log_format(ab, " options="); |
840c5ba4 | 464 | + audit_log_untrustedstring(ab, aad(sa)->mnt.data); |
1e8b8f9b | 465 | + } |
9474138d | 466 | +} |
2380c486 | 467 | + |
fc63ffa9 | 468 | +/** |
1e8b8f9b AM |
469 | + * audit_mount - handle the auditing of mount operations |
470 | + * @profile: the profile being enforced (NOT NULL) | |
1e8b8f9b AM |
471 | + * @op: operation being mediated (NOT NULL) |
472 | + * @name: name of object being mediated (MAYBE NULL) | |
473 | + * @src_name: src_name of object being mediated (MAYBE_NULL) | |
474 | + * @type: type of filesystem (MAYBE_NULL) | |
475 | + * @trans: name of trans (MAYBE NULL) | |
476 | + * @flags: filesystem idependent mount flags | |
477 | + * @data: filesystem mount flags | |
478 | + * @request: permissions requested | |
479 | + * @perms: the permissions computed for the request (NOT NULL) | |
480 | + * @info: extra information message (MAYBE NULL) | |
481 | + * @error: 0 if operation allowed else failure error code | |
9474138d | 482 | + * |
1e8b8f9b | 483 | + * Returns: %0 or error on failure |
76514441 | 484 | + */ |
daaa955e | 485 | +static int audit_mount(struct aa_profile *profile, const char *op, |
1e8b8f9b AM |
486 | + const char *name, const char *src_name, |
487 | + const char *type, const char *trans, | |
488 | + unsigned long flags, const void *data, u32 request, | |
daaa955e | 489 | + struct aa_perms *perms, const char *info, int error) |
2380c486 | 490 | +{ |
1e8b8f9b | 491 | + int audit_type = AUDIT_APPARMOR_AUTO; |
840c5ba4 | 492 | + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); |
9474138d | 493 | + |
1e8b8f9b AM |
494 | + if (likely(!error)) { |
495 | + u32 mask = perms->audit; | |
9474138d | 496 | + |
1e8b8f9b AM |
497 | + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) |
498 | + mask = 0xffff; | |
76514441 | 499 | + |
1e8b8f9b AM |
500 | + /* mask off perms that are not being force audited */ |
501 | + request &= mask; | |
fc63ffa9 | 502 | + |
1e8b8f9b AM |
503 | + if (likely(!request)) |
504 | + return 0; | |
505 | + audit_type = AUDIT_APPARMOR_AUDIT; | |
506 | + } else { | |
507 | + /* only report permissions that were denied */ | |
508 | + request = request & ~perms->allow; | |
509 | + | |
510 | + if (request & perms->kill) | |
511 | + audit_type = AUDIT_APPARMOR_KILL; | |
512 | + | |
513 | + /* quiet known rejects, assumes quiet and kill do not overlap */ | |
514 | + if ((request & perms->quiet) && | |
515 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && | |
516 | + AUDIT_MODE(profile) != AUDIT_ALL) | |
517 | + request &= ~perms->quiet; | |
518 | + | |
519 | + if (!request) | |
daaa955e | 520 | + return error; |
1e8b8f9b AM |
521 | + } |
522 | + | |
840c5ba4 AM |
523 | + aad(&sa)->name = name; |
524 | + aad(&sa)->mnt.src_name = src_name; | |
525 | + aad(&sa)->mnt.type = type; | |
526 | + aad(&sa)->mnt.trans = trans; | |
527 | + aad(&sa)->mnt.flags = flags; | |
1e8b8f9b | 528 | + if (data && (perms->audit & AA_AUDIT_DATA)) |
840c5ba4 AM |
529 | + aad(&sa)->mnt.data = data; |
530 | + aad(&sa)->info = info; | |
531 | + aad(&sa)->error = error; | |
1e8b8f9b | 532 | + |
840c5ba4 | 533 | + return aa_audit(audit_type, profile, &sa, audit_cb); |
76514441 AM |
534 | +} |
535 | + | |
536 | +/** | |
1e8b8f9b AM |
537 | + * match_mnt_flags - Do an ordered match on mount flags |
538 | + * @dfa: dfa to match against | |
539 | + * @state: state to start in | |
540 | + * @flags: mount flags to match against | |
76514441 | 541 | + * |
1e8b8f9b AM |
542 | + * Mount flags are encoded as an ordered match. This is done instead of |
543 | + * checking against a simple bitmask, to allow for logical operations | |
544 | + * on the flags. | |
76514441 | 545 | + * |
1e8b8f9b | 546 | + * Returns: next state after flags match |
76514441 | 547 | + */ |
1e8b8f9b AM |
548 | +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, |
549 | + unsigned long flags) | |
9474138d | 550 | +{ |
1e8b8f9b | 551 | + unsigned int i; |
fc63ffa9 | 552 | + |
1e8b8f9b AM |
553 | + for (i = 0; i <= 31 ; ++i) { |
554 | + if ((1 << i) & flags) | |
555 | + state = aa_dfa_next(dfa, state, i + 1); | |
556 | + } | |
557 | + | |
558 | + return state; | |
9474138d AM |
559 | +} |
560 | + | |
561 | +/** | |
1e8b8f9b AM |
562 | + * compute_mnt_perms - compute mount permission associated with @state |
563 | + * @dfa: dfa to match against (NOT NULL) | |
564 | + * @state: state match finished in | |
ceaf2cfb | 565 | + * |
1e8b8f9b | 566 | + * Returns: mount permissions |
9474138d | 567 | + */ |
daaa955e | 568 | +static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa, |
1e8b8f9b | 569 | + unsigned int state) |
9474138d | 570 | +{ |
daaa955e | 571 | + struct aa_perms perms; |
2380c486 | 572 | + |
1e8b8f9b AM |
573 | + perms.kill = 0; |
574 | + perms.allow = dfa_user_allow(dfa, state); | |
575 | + perms.audit = dfa_user_audit(dfa, state); | |
576 | + perms.quiet = dfa_user_quiet(dfa, state); | |
577 | + perms.xindex = dfa_user_xindex(dfa, state); | |
578 | + | |
579 | + return perms; | |
580 | +} | |
581 | + | |
daaa955e | 582 | +static const char * const mnt_info_table[] = { |
1e8b8f9b AM |
583 | + "match succeeded", |
584 | + "failed mntpnt match", | |
585 | + "failed srcname match", | |
586 | + "failed type match", | |
587 | + "failed flags match", | |
588 | + "failed data match" | |
589 | +}; | |
590 | + | |
591 | +/* | |
592 | + * Returns 0 on success else element that match failed in, this is the | |
593 | + * index into the mnt_info_table above | |
594 | + */ | |
595 | +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, | |
596 | + const char *mntpnt, const char *devname, | |
597 | + const char *type, unsigned long flags, | |
daaa955e | 598 | + void *data, bool binary, struct aa_perms *perms) |
1e8b8f9b AM |
599 | +{ |
600 | + unsigned int state; | |
601 | + | |
daaa955e AM |
602 | + AA_BUG(!dfa); |
603 | + AA_BUG(!perms); | |
604 | + | |
1e8b8f9b AM |
605 | + state = aa_dfa_match(dfa, start, mntpnt); |
606 | + state = aa_dfa_null_transition(dfa, state); | |
607 | + if (!state) | |
608 | + return 1; | |
609 | + | |
610 | + if (devname) | |
611 | + state = aa_dfa_match(dfa, state, devname); | |
612 | + state = aa_dfa_null_transition(dfa, state); | |
613 | + if (!state) | |
614 | + return 2; | |
615 | + | |
616 | + if (type) | |
617 | + state = aa_dfa_match(dfa, state, type); | |
618 | + state = aa_dfa_null_transition(dfa, state); | |
619 | + if (!state) | |
620 | + return 3; | |
621 | + | |
622 | + state = match_mnt_flags(dfa, state, flags); | |
623 | + if (!state) | |
624 | + return 4; | |
625 | + *perms = compute_mnt_perms(dfa, state); | |
626 | + if (perms->allow & AA_MAY_MOUNT) | |
627 | + return 0; | |
628 | + | |
629 | + /* only match data if not binary and the DFA flags data is expected */ | |
daaa955e | 630 | + if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) { |
1e8b8f9b AM |
631 | + state = aa_dfa_null_transition(dfa, state); |
632 | + if (!state) | |
633 | + return 4; | |
634 | + | |
635 | + state = aa_dfa_match(dfa, state, data); | |
636 | + if (!state) | |
637 | + return 5; | |
638 | + *perms = compute_mnt_perms(dfa, state); | |
639 | + if (perms->allow & AA_MAY_MOUNT) | |
640 | + return 0; | |
fc63ffa9 | 641 | + } |
1e8b8f9b AM |
642 | + |
643 | + /* failed at end of flags match */ | |
644 | + return 4; | |
9474138d AM |
645 | +} |
646 | + | |
daaa955e AM |
647 | + |
648 | +static int path_flags(struct aa_profile *profile, const struct path *path) | |
649 | +{ | |
650 | + AA_BUG(!profile); | |
651 | + AA_BUG(!path); | |
652 | + | |
653 | + return profile->path_flags | | |
654 | + (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0); | |
655 | +} | |
656 | + | |
ceaf2cfb | 657 | +/** |
daaa955e | 658 | + * match_mnt_path_str - handle path matching for mount |
1e8b8f9b | 659 | + * @profile: the confining profile |
daaa955e AM |
660 | + * @mntpath: for the mntpnt (NOT NULL) |
661 | + * @buffer: buffer to be used to lookup mntpath | |
662 | + * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR) | |
1e8b8f9b AM |
663 | + * @type: string for the dev type (MAYBE NULL) |
664 | + * @flags: mount flags to match | |
665 | + * @data: fs mount data (MAYBE NULL) | |
666 | + * @binary: whether @data is binary | |
daaa955e | 667 | + * @devinfo: error str if (IS_ERR(@devname)) |
fc63ffa9 | 668 | + * |
1e8b8f9b | 669 | + * Returns: 0 on success else error |
ceaf2cfb | 670 | + */ |
daaa955e AM |
671 | +static int match_mnt_path_str(struct aa_profile *profile, |
672 | + const struct path *mntpath, char *buffer, | |
673 | + const char *devname, const char *type, | |
674 | + unsigned long flags, void *data, bool binary, | |
675 | + const char *devinfo) | |
9474138d | 676 | +{ |
daaa955e AM |
677 | + struct aa_perms perms = { }; |
678 | + const char *mntpnt = NULL, *info = NULL; | |
679 | + int pos, error; | |
2380c486 | 680 | + |
daaa955e AM |
681 | + AA_BUG(!profile); |
682 | + AA_BUG(!mntpath); | |
683 | + AA_BUG(!buffer); | |
684 | + | |
685 | + error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer, | |
686 | + &mntpnt, &info, profile->disconnected); | |
687 | + if (error) | |
688 | + goto audit; | |
689 | + if (IS_ERR(devname)) { | |
690 | + error = PTR_ERR(devname); | |
691 | + devname = NULL; | |
692 | + info = devinfo; | |
693 | + goto audit; | |
694 | + } | |
1e8b8f9b | 695 | + |
daaa955e | 696 | + error = -EACCES; |
1e8b8f9b AM |
697 | + pos = do_match_mnt(profile->policy.dfa, |
698 | + profile->policy.start[AA_CLASS_MOUNT], | |
daaa955e | 699 | + mntpnt, devname, type, flags, data, binary, &perms); |
1e8b8f9b | 700 | + if (pos) { |
daaa955e AM |
701 | + info = mnt_info_table[pos]; |
702 | + goto audit; | |
1e8b8f9b | 703 | + } |
daaa955e | 704 | + error = 0; |
2380c486 | 705 | + |
daaa955e AM |
706 | +audit: |
707 | + return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL, | |
708 | + flags, data, AA_MAY_MOUNT, &perms, info, error); | |
9474138d | 709 | +} |
9474138d | 710 | + |
daaa955e AM |
711 | +/** |
712 | + * match_mnt - handle path matching for mount | |
713 | + * @profile: the confining profile | |
714 | + * @mntpath: for the mntpnt (NOT NULL) | |
715 | + * @buffer: buffer to be used to lookup mntpath | |
716 | + * @devpath: path devname/src_name (MAYBE NULL) | |
717 | + * @devbuffer: buffer to be used to lookup devname/src_name | |
718 | + * @type: string for the dev type (MAYBE NULL) | |
719 | + * @flags: mount flags to match | |
720 | + * @data: fs mount data (MAYBE NULL) | |
721 | + * @binary: whether @data is binary | |
722 | + * | |
723 | + * Returns: 0 on success else error | |
724 | + */ | |
725 | +static int match_mnt(struct aa_profile *profile, const struct path *path, | |
726 | + char *buffer, struct path *devpath, char *devbuffer, | |
727 | + const char *type, unsigned long flags, void *data, | |
728 | + bool binary) | |
1e8b8f9b | 729 | +{ |
daaa955e AM |
730 | + const char *devname = NULL, *info = NULL; |
731 | + int error = -EACCES; | |
732 | + | |
733 | + AA_BUG(!profile); | |
734 | + AA_BUG(devpath && !devbuffer); | |
735 | + | |
736 | + if (devpath) { | |
737 | + error = aa_path_name(devpath, path_flags(profile, devpath), | |
738 | + devbuffer, &devname, &info, | |
739 | + profile->disconnected); | |
740 | + if (error) | |
741 | + devname = ERR_PTR(error); | |
742 | + } | |
743 | + | |
744 | + return match_mnt_path_str(profile, path, buffer, devname, type, flags, | |
745 | + data, binary, info); | |
1e8b8f9b | 746 | +} |
9474138d | 747 | + |
daaa955e | 748 | +int aa_remount(struct aa_label *label, const struct path *path, |
1e8b8f9b | 749 | + unsigned long flags, void *data) |
9474138d | 750 | +{ |
daaa955e | 751 | + struct aa_profile *profile; |
1e8b8f9b | 752 | + char *buffer = NULL; |
daaa955e AM |
753 | + bool binary; |
754 | + int error; | |
1e8b8f9b | 755 | + |
daaa955e AM |
756 | + AA_BUG(!label); |
757 | + AA_BUG(!path); | |
1e8b8f9b | 758 | + |
daaa955e | 759 | + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; |
1e8b8f9b | 760 | + |
daaa955e AM |
761 | + get_buffers(buffer); |
762 | + error = fn_for_each_confined(label, profile, | |
763 | + match_mnt(profile, path, buffer, NULL, NULL, NULL, | |
764 | + flags, data, binary)); | |
765 | + put_buffers(buffer); | |
1e8b8f9b AM |
766 | + |
767 | + return error; | |
9474138d AM |
768 | +} |
769 | + | |
daaa955e | 770 | +int aa_bind_mount(struct aa_label *label, const struct path *path, |
1e8b8f9b | 771 | + const char *dev_name, unsigned long flags) |
9474138d | 772 | +{ |
daaa955e | 773 | + struct aa_profile *profile; |
1e8b8f9b | 774 | + char *buffer = NULL, *old_buffer = NULL; |
1e8b8f9b AM |
775 | + struct path old_path; |
776 | + int error; | |
777 | + | |
daaa955e AM |
778 | + AA_BUG(!label); |
779 | + AA_BUG(!path); | |
780 | + | |
1e8b8f9b AM |
781 | + if (!dev_name || !*dev_name) |
782 | + return -EINVAL; | |
783 | + | |
784 | + flags &= MS_REC | MS_BIND; | |
785 | + | |
1e8b8f9b AM |
786 | + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); |
787 | + if (error) | |
daaa955e | 788 | + return error; |
1e8b8f9b | 789 | + |
daaa955e AM |
790 | + get_buffers(buffer, old_buffer); |
791 | + error = fn_for_each_confined(label, profile, | |
792 | + match_mnt(profile, path, buffer, &old_path, old_buffer, | |
793 | + NULL, flags, NULL, false)); | |
794 | + put_buffers(buffer, old_buffer); | |
1e8b8f9b | 795 | + path_put(&old_path); |
1e8b8f9b AM |
796 | + |
797 | + return error; | |
9474138d | 798 | +} |
fc63ffa9 | 799 | + |
daaa955e | 800 | +int aa_mount_change_type(struct aa_label *label, const struct path *path, |
1e8b8f9b AM |
801 | + unsigned long flags) |
802 | +{ | |
daaa955e | 803 | + struct aa_profile *profile; |
1e8b8f9b | 804 | + char *buffer = NULL; |
1e8b8f9b AM |
805 | + int error; |
806 | + | |
daaa955e AM |
807 | + AA_BUG(!label); |
808 | + AA_BUG(!path); | |
809 | + | |
1e8b8f9b AM |
810 | + /* These are the flags allowed by do_change_type() */ |
811 | + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | | |
812 | + MS_UNBINDABLE); | |
813 | + | |
daaa955e AM |
814 | + get_buffers(buffer); |
815 | + error = fn_for_each_confined(label, profile, | |
816 | + match_mnt(profile, path, buffer, NULL, NULL, NULL, | |
817 | + flags, NULL, false)); | |
818 | + put_buffers(buffer); | |
1e8b8f9b AM |
819 | + |
820 | + return error; | |
821 | +} | |
822 | + | |
daaa955e | 823 | +int aa_move_mount(struct aa_label *label, const struct path *path, |
1e8b8f9b AM |
824 | + const char *orig_name) |
825 | +{ | |
daaa955e | 826 | + struct aa_profile *profile; |
1e8b8f9b | 827 | + char *buffer = NULL, *old_buffer = NULL; |
1e8b8f9b AM |
828 | + struct path old_path; |
829 | + int error; | |
830 | + | |
daaa955e AM |
831 | + AA_BUG(!label); |
832 | + AA_BUG(!path); | |
833 | + | |
1e8b8f9b AM |
834 | + if (!orig_name || !*orig_name) |
835 | + return -EINVAL; | |
836 | + | |
1e8b8f9b | 837 | + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); |
fc63ffa9 | 838 | + if (error) |
daaa955e | 839 | + return error; |
1e8b8f9b | 840 | + |
daaa955e AM |
841 | + get_buffers(buffer, old_buffer); |
842 | + error = fn_for_each_confined(label, profile, | |
843 | + match_mnt(profile, path, buffer, &old_path, old_buffer, | |
844 | + NULL, MS_MOVE, NULL, false)); | |
845 | + put_buffers(buffer, old_buffer); | |
1e8b8f9b | 846 | + path_put(&old_path); |
1e8b8f9b AM |
847 | + |
848 | + return error; | |
849 | +} | |
850 | + | |
daaa955e | 851 | +int aa_new_mount(struct aa_label *label, const char *dev_name, |
0776672e | 852 | + const struct path *path, const char *type, unsigned long flags, |
1e8b8f9b AM |
853 | + void *data) |
854 | +{ | |
daaa955e | 855 | + struct aa_profile *profile; |
1e8b8f9b | 856 | + char *buffer = NULL, *dev_buffer = NULL; |
daaa955e | 857 | + bool binary = true; |
1e8b8f9b | 858 | + int error; |
daaa955e AM |
859 | + int requires_dev = 0; |
860 | + struct path tmp_path, *dev_path = NULL; | |
861 | + | |
862 | + AA_BUG(!label); | |
863 | + AA_BUG(!path); | |
1e8b8f9b | 864 | + |
1e8b8f9b | 865 | + if (type) { |
daaa955e AM |
866 | + struct file_system_type *fstype; |
867 | + | |
868 | + fstype = get_fs_type(type); | |
1e8b8f9b AM |
869 | + if (!fstype) |
870 | + return -ENODEV; | |
1e8b8f9b AM |
871 | + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; |
872 | + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; | |
873 | + put_filesystem(fstype); | |
874 | + | |
875 | + if (requires_dev) { | |
daaa955e AM |
876 | + if (!dev_name || !*dev_name) |
877 | + return -ENOENT; | |
1e8b8f9b | 878 | + |
daaa955e | 879 | + error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path); |
1e8b8f9b | 880 | + if (error) |
daaa955e AM |
881 | + return error; |
882 | + dev_path = &tmp_path; | |
1e8b8f9b AM |
883 | + } |
884 | + } | |
885 | + | |
daaa955e AM |
886 | + get_buffers(buffer, dev_buffer); |
887 | + if (dev_path) { | |
888 | + error = fn_for_each_confined(label, profile, | |
889 | + match_mnt(profile, path, buffer, dev_path, dev_buffer, | |
890 | + type, flags, data, binary)); | |
891 | + } else { | |
892 | + error = fn_for_each_confined(label, profile, | |
893 | + match_mnt_path_str(profile, path, buffer, dev_name, | |
894 | + type, flags, data, binary, NULL)); | |
895 | + } | |
896 | + put_buffers(buffer, dev_buffer); | |
897 | + if (dev_path) | |
898 | + path_put(dev_path); | |
1e8b8f9b | 899 | + |
1e8b8f9b | 900 | + return error; |
1e8b8f9b AM |
901 | +} |
902 | + | |
daaa955e AM |
903 | +static int profile_umount(struct aa_profile *profile, struct path *path, |
904 | + char *buffer) | |
1e8b8f9b | 905 | +{ |
daaa955e AM |
906 | + struct aa_perms perms = { }; |
907 | + const char *name = NULL, *info = NULL; | |
908 | + unsigned int state; | |
1e8b8f9b AM |
909 | + int error; |
910 | + | |
daaa955e AM |
911 | + AA_BUG(!profile); |
912 | + AA_BUG(!path); | |
913 | + | |
914 | + error = aa_path_name(path, path_flags(profile, path), buffer, &name, | |
915 | + &info, profile->disconnected); | |
1e8b8f9b AM |
916 | + if (error) |
917 | + goto audit; | |
918 | + | |
daaa955e AM |
919 | + state = aa_dfa_match(profile->policy.dfa, |
920 | + profile->policy.start[AA_CLASS_MOUNT], | |
921 | + name); | |
922 | + perms = compute_mnt_perms(profile->policy.dfa, state); | |
1e8b8f9b AM |
923 | + if (AA_MAY_UMOUNT & ~perms.allow) |
924 | + error = -EACCES; | |
925 | + | |
926 | +audit: | |
daaa955e AM |
927 | + return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL, |
928 | + AA_MAY_UMOUNT, &perms, info, error); | |
929 | +} | |
930 | + | |
931 | +int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags) | |
932 | +{ | |
933 | + struct aa_profile *profile; | |
934 | + char *buffer = NULL; | |
935 | + int error; | |
936 | + struct path path = { .mnt = mnt, .dentry = mnt->mnt_root }; | |
937 | + | |
938 | + AA_BUG(!label); | |
939 | + AA_BUG(!mnt); | |
940 | + | |
941 | + get_buffers(buffer); | |
942 | + error = fn_for_each_confined(label, profile, | |
943 | + profile_umount(profile, &path, buffer)); | |
944 | + put_buffers(buffer); | |
1e8b8f9b AM |
945 | + |
946 | + return error; | |
947 | +} | |
948 | + | |
daaa955e AM |
949 | +/* helper fn for transition on pivotroot |
950 | + * | |
951 | + * Returns: label for transition or ERR_PTR. Does not return NULL | |
952 | + */ | |
953 | +static struct aa_label *build_pivotroot(struct aa_profile *profile, | |
954 | + const struct path *new_path, | |
955 | + char *new_buffer, | |
956 | + const struct path *old_path, | |
957 | + char *old_buffer) | |
1e8b8f9b | 958 | +{ |
1e8b8f9b | 959 | + const char *old_name, *new_name = NULL, *info = NULL; |
daaa955e AM |
960 | + const char *trans_name = NULL; |
961 | + struct aa_perms perms = { }; | |
962 | + unsigned int state; | |
1e8b8f9b AM |
963 | + int error; |
964 | + | |
daaa955e AM |
965 | + AA_BUG(!profile); |
966 | + AA_BUG(!new_path); | |
967 | + AA_BUG(!old_path); | |
968 | + | |
969 | + if (profile_unconfined(profile)) | |
970 | + return aa_get_newest_label(&profile->label); | |
971 | + | |
1e8b8f9b | 972 | + error = aa_path_name(old_path, path_flags(profile, old_path), |
daaa955e AM |
973 | + old_buffer, &old_name, &info, |
974 | + profile->disconnected); | |
1e8b8f9b AM |
975 | + if (error) |
976 | + goto audit; | |
1e8b8f9b | 977 | + error = aa_path_name(new_path, path_flags(profile, new_path), |
daaa955e AM |
978 | + new_buffer, &new_name, &info, |
979 | + profile->disconnected); | |
1e8b8f9b AM |
980 | + if (error) |
981 | + goto audit; | |
982 | + | |
daaa955e AM |
983 | + error = -EACCES; |
984 | + state = aa_dfa_match(profile->policy.dfa, | |
985 | + profile->policy.start[AA_CLASS_MOUNT], | |
986 | + new_name); | |
987 | + state = aa_dfa_null_transition(profile->policy.dfa, state); | |
988 | + state = aa_dfa_match(profile->policy.dfa, state, old_name); | |
989 | + perms = compute_mnt_perms(profile->policy.dfa, state); | |
1e8b8f9b | 990 | + |
daaa955e AM |
991 | + if (AA_MAY_PIVOTROOT & perms.allow) |
992 | + error = 0; | |
1e8b8f9b AM |
993 | + |
994 | +audit: | |
daaa955e AM |
995 | + error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name, |
996 | + NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT, | |
997 | + &perms, info, error); | |
998 | + if (error) | |
999 | + return ERR_PTR(error); | |
1000 | + | |
1001 | + return aa_get_newest_label(&profile->label); | |
1002 | +} | |
1003 | + | |
1004 | +int aa_pivotroot(struct aa_label *label, const struct path *old_path, | |
1005 | + const struct path *new_path) | |
1006 | +{ | |
1007 | + struct aa_profile *profile; | |
1008 | + struct aa_label *target = NULL; | |
1009 | + char *old_buffer = NULL, *new_buffer = NULL, *info = NULL; | |
1010 | + int error; | |
1011 | + | |
1012 | + AA_BUG(!label); | |
1013 | + AA_BUG(!old_path); | |
1014 | + AA_BUG(!new_path); | |
1015 | + | |
1016 | + get_buffers(old_buffer, new_buffer); | |
1017 | + target = fn_label_build(label, profile, GFP_ATOMIC, | |
1018 | + build_pivotroot(profile, new_path, new_buffer, | |
1019 | + old_path, old_buffer)); | |
1020 | + if (!target) { | |
1021 | + info = "label build failed"; | |
1022 | + error = -ENOMEM; | |
1023 | + goto fail; | |
1024 | + } else if (!IS_ERR(target)) { | |
1025 | + error = aa_replace_current_label(target); | |
1026 | + if (error) { | |
1027 | + /* TODO: audit target */ | |
1028 | + aa_put_label(target); | |
1029 | + goto out; | |
1030 | + } | |
1031 | + } else | |
1032 | + /* already audited error */ | |
1033 | + error = PTR_ERR(target); | |
1034 | +out: | |
1035 | + put_buffers(old_buffer, new_buffer); | |
1e8b8f9b AM |
1036 | + |
1037 | + return error; | |
daaa955e AM |
1038 | + |
1039 | +fail: | |
1040 | + /* TODO: add back in auditing of new_name and old_name */ | |
1041 | + error = fn_for_each(label, profile, | |
1042 | + audit_mount(profile, OP_PIVOTROOT, NULL /*new_name */, | |
1043 | + NULL /* old_name */, | |
1044 | + NULL, NULL, | |
1045 | + 0, NULL, AA_MAY_PIVOTROOT, &nullperms, info, | |
1046 | + error)); | |
1047 | + goto out; | |
1e8b8f9b | 1048 | +} |
daaa955e AM |
1049 | -- |
1050 | 2.11.0 | |
1051 |