]> git.pld-linux.org Git - packages/kernel.git/blame - 0007-apparmor-add-mount-mediation.patch
- drop obsolete files
[packages/kernel.git] / 0007-apparmor-add-mount-mediation.patch
CommitLineData
daaa955e
AM
1From f37356d0a41499f9222f9f2b9c0147b500ae4285 Mon Sep 17 00:00:00 2001
2From: John Johansen <john.johansen@canonical.com>
3Date: Tue, 18 Jul 2017 23:04:47 -0700
4Subject: [PATCH 07/17] apparmor: add mount mediation
1e8b8f9b 5
daaa955e
AM
6Add basic mount mediation. That allows controlling based on basic
7mount parameters. It does not include special mount parameters for
8apparmor, super block labeling, or any triggers for apparmor namespace
9parameter modifications on pivot root.
10
11default 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
54eg.
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
61Signed-off-by: John Johansen <john.johansen@canonical.com>
62Acked-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 78diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
daaa955e 79index 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 91diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
daaa955e 92index 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 119diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
daaa955e 120index 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 134diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
daaa955e 135index 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 146diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
daaa955e 147index 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 175diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h
daaa955e 176index 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 198diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
ceaf2cfb 199new file mode 100644
daaa955e 200index 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 */
258diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
daaa955e 259index 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
347diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
348new file mode 100644
daaa955e 349index 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--
10502.11.0
1051
This page took 0.288728 seconds and 4 git commands to generate.