aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Johansen <john.johansen@canonical.com>2017-07-19 02:04:47 -0400
committerJohn Johansen <john.johansen@canonical.com>2017-09-22 16:00:57 -0400
commit2ea3ffb7782a84da33a8382f13ebd016da50079b (patch)
tree40ad5dff6b1b6750aa36daf7fbecb7430ca9a3c0
parentcd1dbf76b23d5ab2cba5e657fe20b1e236a408cc (diff)
apparmor: add mount mediation
Add basic mount mediation. That allows controlling based on basic mount parameters. It does not include special mount parameters for apparmor, super block labeling, or any triggers for apparmor namespace parameter modifications on pivot root. default userspace policy rules have the form of MOUNT RULE = ( MOUNT | REMOUNT | UMOUNT ) MOUNT = [ QUALIFIERS ] 'mount' [ MOUNT CONDITIONS ] [ SOURCE FILEGLOB ] [ '->' MOUNTPOINT FILEGLOB ] REMOUNT = [ QUALIFIERS ] 'remount' [ MOUNT CONDITIONS ] MOUNTPOINT FILEGLOB UMOUNT = [ QUALIFIERS ] 'umount' [ MOUNT CONDITIONS ] MOUNTPOINT FILEGLOB MOUNT CONDITIONS = [ ( 'fstype' | 'vfstype' ) ( '=' | 'in' ) MOUNT FSTYPE EXPRESSION ] [ 'options' ( '=' | 'in' ) MOUNT FLAGS EXPRESSION ] MOUNT FSTYPE EXPRESSION = ( MOUNT FSTYPE LIST | MOUNT EXPRESSION ) MOUNT FSTYPE LIST = Comma separated list of valid filesystem and virtual filesystem types (eg ext4, debugfs, etc) MOUNT FLAGS EXPRESSION = ( MOUNT FLAGS LIST | MOUNT EXPRESSION ) MOUNT FLAGS LIST = Comma separated list of MOUNT FLAGS. MOUNT FLAGS = ( 'ro' | 'rw' | 'nosuid' | 'suid' | 'nodev' | 'dev' | 'noexec' | 'exec' | 'sync' | 'async' | 'remount' | 'mand' | 'nomand' | 'dirsync' | 'noatime' | 'atime' | 'nodiratime' | 'diratime' | 'bind' | 'rbind' | 'move' | 'verbose' | 'silent' | 'loud' | 'acl' | 'noacl' | 'unbindable' | 'runbindable' | 'private' | 'rprivate' | 'slave' | 'rslave' | 'shared' | 'rshared' | 'relatime' | 'norelatime' | 'iversion' | 'noiversion' | 'strictatime' | 'nouser' | 'user' ) MOUNT EXPRESSION = ( ALPHANUMERIC | AARE ) ... PIVOT ROOT RULE = [ QUALIFIERS ] pivot_root [ oldroot=OLD PUT FILEGLOB ] [ NEW ROOT FILEGLOB ] SOURCE FILEGLOB = FILEGLOB MOUNTPOINT FILEGLOB = FILEGLOB eg. mount, mount /dev/foo, mount options=ro /dev/foo -> /mnt/, mount options in (ro,atime) /dev/foo -> /mnt/, mount options=ro options=atime, Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
-rw-r--r--security/apparmor/Makefile2
-rw-r--r--security/apparmor/apparmorfs.c8
-rw-r--r--security/apparmor/domain.c4
-rw-r--r--security/apparmor/include/apparmor.h1
-rw-r--r--security/apparmor/include/audit.h11
-rw-r--r--security/apparmor/include/domain.h5
-rw-r--r--security/apparmor/include/mount.h54
-rw-r--r--security/apparmor/lsm.c64
-rw-r--r--security/apparmor/mount.c696
9 files changed, 841 insertions, 4 deletions
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index a16b195274de..81a34426d024 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
4 4
5apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ 5apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
6 path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ 6 path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
7 resource.o secid.o file.o policy_ns.o label.o 7 resource.o secid.o file.o policy_ns.o label.o mount.o
8apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o 8apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
9 9
10clean-files := capability_names.h rlim_names.h 10clean-files := capability_names.h rlim_names.h
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index a5f9e1aa51f7..8fa6c898c44b 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -2159,9 +2159,14 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
2159 { } 2159 { }
2160}; 2160};
2161 2161
2162static struct aa_sfs_entry aa_sfs_entry_mount[] = {
2163 AA_SFS_FILE_STRING("mask", "mount umount pivot_root"),
2164 { }
2165};
2166
2162static struct aa_sfs_entry aa_sfs_entry_ns[] = { 2167static struct aa_sfs_entry aa_sfs_entry_ns[] = {
2163 AA_SFS_FILE_BOOLEAN("profile", 1), 2168 AA_SFS_FILE_BOOLEAN("profile", 1),
2164 AA_SFS_FILE_BOOLEAN("pivot_root", 1), 2169 AA_SFS_FILE_BOOLEAN("pivot_root", 0),
2165 { } 2170 { }
2166}; 2171};
2167 2172
@@ -2180,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
2180 AA_SFS_DIR("policy", aa_sfs_entry_policy), 2185 AA_SFS_DIR("policy", aa_sfs_entry_policy),
2181 AA_SFS_DIR("domain", aa_sfs_entry_domain), 2186 AA_SFS_DIR("domain", aa_sfs_entry_domain),
2182 AA_SFS_DIR("file", aa_sfs_entry_file), 2187 AA_SFS_DIR("file", aa_sfs_entry_file),
2188 AA_SFS_DIR("mount", aa_sfs_entry_mount),
2183 AA_SFS_DIR("namespaces", aa_sfs_entry_ns), 2189 AA_SFS_DIR("namespaces", aa_sfs_entry_ns),
2184 AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), 2190 AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
2185 AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit), 2191 AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit),
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index d0594446ae3f..ffc8c75a6785 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -374,8 +374,8 @@ static const char *next_name(int xtype, const char *name)
374 * 374 *
375 * Returns: refcounted label, or NULL on failure (MAYBE NULL) 375 * Returns: refcounted label, or NULL on failure (MAYBE NULL)
376 */ 376 */
377static struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, 377struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
378 const char **name) 378 const char **name)
379{ 379{
380 struct aa_label *label = NULL; 380 struct aa_label *label = NULL;
381 u32 xtype = xindex & AA_X_TYPE_MASK; 381 u32 xtype = xindex & AA_X_TYPE_MASK;
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index 962a20a75e01..829082c35faa 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -27,6 +27,7 @@
27#define AA_CLASS_NET 4 27#define AA_CLASS_NET 4
28#define AA_CLASS_RLIMITS 5 28#define AA_CLASS_RLIMITS 5
29#define AA_CLASS_DOMAIN 6 29#define AA_CLASS_DOMAIN 6
30#define AA_CLASS_MOUNT 7
30#define AA_CLASS_PTRACE 9 31#define AA_CLASS_PTRACE 9
31#define AA_CLASS_SIGNAL 10 32#define AA_CLASS_SIGNAL 10
32#define AA_CLASS_LABEL 16 33#define AA_CLASS_LABEL 16
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
index d9a156ae11b9..c3fe1c5ef3bc 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -71,6 +71,10 @@ enum audit_type {
71#define OP_FMPROT "file_mprotect" 71#define OP_FMPROT "file_mprotect"
72#define OP_INHERIT "file_inherit" 72#define OP_INHERIT "file_inherit"
73 73
74#define OP_PIVOTROOT "pivotroot"
75#define OP_MOUNT "mount"
76#define OP_UMOUNT "umount"
77
74#define OP_CREATE "create" 78#define OP_CREATE "create"
75#define OP_POST_CREATE "post_create" 79#define OP_POST_CREATE "post_create"
76#define OP_BIND "bind" 80#define OP_BIND "bind"
@@ -132,6 +136,13 @@ struct apparmor_audit_data {
132 int rlim; 136 int rlim;
133 unsigned long max; 137 unsigned long max;
134 } rlim; 138 } rlim;
139 struct {
140 const char *src_name;
141 const char *type;
142 const char *trans;
143 const char *data;
144 unsigned long flags;
145 } mnt;
135 }; 146 };
136}; 147};
137 148
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h
index bab5810b6e9a..db27403346c5 100644
--- a/security/apparmor/include/domain.h
+++ b/security/apparmor/include/domain.h
@@ -15,6 +15,8 @@
15#include <linux/binfmts.h> 15#include <linux/binfmts.h>
16#include <linux/types.h> 16#include <linux/types.h>
17 17
18#include "label.h"
19
18#ifndef __AA_DOMAIN_H 20#ifndef __AA_DOMAIN_H
19#define __AA_DOMAIN_H 21#define __AA_DOMAIN_H
20 22
@@ -29,6 +31,9 @@ struct aa_domain {
29#define AA_CHANGE_ONEXEC 4 31#define AA_CHANGE_ONEXEC 4
30#define AA_CHANGE_STACK 8 32#define AA_CHANGE_STACK 8
31 33
34struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
35 const char **name);
36
32int apparmor_bprm_set_creds(struct linux_binprm *bprm); 37int apparmor_bprm_set_creds(struct linux_binprm *bprm);
33int apparmor_bprm_secureexec(struct linux_binprm *bprm); 38int apparmor_bprm_secureexec(struct linux_binprm *bprm);
34 39
diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
new file mode 100644
index 000000000000..25d6067fa6ef
--- /dev/null
+++ b/security/apparmor/include/mount.h
@@ -0,0 +1,54 @@
1/*
2 * AppArmor security module
3 *
4 * This file contains AppArmor file mediation function definitions.
5 *
6 * Copyright 2017 Canonical Ltd.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation, version 2 of the
11 * License.
12 */
13
14#ifndef __AA_MOUNT_H
15#define __AA_MOUNT_H
16
17#include <linux/fs.h>
18#include <linux/path.h>
19
20#include "domain.h"
21#include "policy.h"
22
23/* mount perms */
24#define AA_MAY_PIVOTROOT 0x01
25#define AA_MAY_MOUNT 0x02
26#define AA_MAY_UMOUNT 0x04
27#define AA_AUDIT_DATA 0x40
28#define AA_MNT_CONT_MATCH 0x40
29
30#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN)
31
32int aa_remount(struct aa_label *label, const struct path *path,
33 unsigned long flags, void *data);
34
35int aa_bind_mount(struct aa_label *label, const struct path *path,
36 const char *old_name, unsigned long flags);
37
38
39int aa_mount_change_type(struct aa_label *label, const struct path *path,
40 unsigned long flags);
41
42int aa_move_mount(struct aa_label *label, const struct path *path,
43 const char *old_name);
44
45int aa_new_mount(struct aa_label *label, const char *dev_name,
46 const struct path *path, const char *type, unsigned long flags,
47 void *data);
48
49int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags);
50
51int aa_pivotroot(struct aa_label *label, const struct path *old_path,
52 const struct path *new_path);
53
54#endif /* __AA_MOUNT_H */
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index af22f3dfbcce..4ad0b3a45142 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -38,6 +38,7 @@
38#include "include/policy.h" 38#include "include/policy.h"
39#include "include/policy_ns.h" 39#include "include/policy_ns.h"
40#include "include/procattr.h" 40#include "include/procattr.h"
41#include "include/mount.h"
41 42
42/* Flag indicating whether initialization completed */ 43/* Flag indicating whether initialization completed */
43int apparmor_initialized; 44int apparmor_initialized;
@@ -511,6 +512,65 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma,
511 !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); 512 !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
512} 513}
513 514
515static int apparmor_sb_mount(const char *dev_name, const struct path *path,
516 const char *type, unsigned long flags, void *data)
517{
518 struct aa_label *label;
519 int error = 0;
520
521 /* Discard magic */
522 if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
523 flags &= ~MS_MGC_MSK;
524
525 flags &= ~AA_MS_IGNORE_MASK;
526
527 label = __begin_current_label_crit_section();
528 if (!unconfined(label)) {
529 if (flags & MS_REMOUNT)
530 error = aa_remount(label, path, flags, data);
531 else if (flags & MS_BIND)
532 error = aa_bind_mount(label, path, dev_name, flags);
533 else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE |
534 MS_UNBINDABLE))
535 error = aa_mount_change_type(label, path, flags);
536 else if (flags & MS_MOVE)
537 error = aa_move_mount(label, path, dev_name);
538 else
539 error = aa_new_mount(label, dev_name, path, type,
540 flags, data);
541 }
542 __end_current_label_crit_section(label);
543
544 return error;
545}
546
547static int apparmor_sb_umount(struct vfsmount *mnt, int flags)
548{
549 struct aa_label *label;
550 int error = 0;
551
552 label = __begin_current_label_crit_section();
553 if (!unconfined(label))
554 error = aa_umount(label, mnt, flags);
555 __end_current_label_crit_section(label);
556
557 return error;
558}
559
560static int apparmor_sb_pivotroot(const struct path *old_path,
561 const struct path *new_path)
562{
563 struct aa_label *label;
564 int error = 0;
565
566 label = aa_get_current_label();
567 if (!unconfined(label))
568 error = aa_pivotroot(label, old_path, new_path);
569 aa_put_label(label);
570
571 return error;
572}
573
514static int apparmor_getprocattr(struct task_struct *task, char *name, 574static int apparmor_getprocattr(struct task_struct *task, char *name,
515 char **value) 575 char **value)
516{ 576{
@@ -682,6 +742,10 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
682 LSM_HOOK_INIT(capget, apparmor_capget), 742 LSM_HOOK_INIT(capget, apparmor_capget),
683 LSM_HOOK_INIT(capable, apparmor_capable), 743 LSM_HOOK_INIT(capable, apparmor_capable),
684 744
745 LSM_HOOK_INIT(sb_mount, apparmor_sb_mount),
746 LSM_HOOK_INIT(sb_umount, apparmor_sb_umount),
747 LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot),
748
685 LSM_HOOK_INIT(path_link, apparmor_path_link), 749 LSM_HOOK_INIT(path_link, apparmor_path_link),
686 LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), 750 LSM_HOOK_INIT(path_unlink, apparmor_path_unlink),
687 LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), 751 LSM_HOOK_INIT(path_symlink, apparmor_path_symlink),
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
new file mode 100644
index 000000000000..82a64b58041d
--- /dev/null
+++ b/security/apparmor/mount.c
@@ -0,0 +1,696 @@
1/*
2 * AppArmor security module
3 *
4 * This file contains AppArmor mediation of files
5 *
6 * Copyright (C) 1998-2008 Novell/SUSE
7 * Copyright 2009-2017 Canonical Ltd.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation, version 2 of the
12 * License.
13 */
14
15#include <linux/fs.h>
16#include <linux/mount.h>
17#include <linux/namei.h>
18
19#include "include/apparmor.h"
20#include "include/audit.h"
21#include "include/context.h"
22#include "include/domain.h"
23#include "include/file.h"
24#include "include/match.h"
25#include "include/mount.h"
26#include "include/path.h"
27#include "include/policy.h"
28
29
30static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
31{
32 if (flags & MS_RDONLY)
33 audit_log_format(ab, "ro");
34 else
35 audit_log_format(ab, "rw");
36 if (flags & MS_NOSUID)
37 audit_log_format(ab, ", nosuid");
38 if (flags & MS_NODEV)
39 audit_log_format(ab, ", nodev");
40 if (flags & MS_NOEXEC)
41 audit_log_format(ab, ", noexec");
42 if (flags & MS_SYNCHRONOUS)
43 audit_log_format(ab, ", sync");
44 if (flags & MS_REMOUNT)
45 audit_log_format(ab, ", remount");
46 if (flags & MS_MANDLOCK)
47 audit_log_format(ab, ", mand");
48 if (flags & MS_DIRSYNC)
49 audit_log_format(ab, ", dirsync");
50 if (flags & MS_NOATIME)
51 audit_log_format(ab, ", noatime");
52 if (flags & MS_NODIRATIME)
53 audit_log_format(ab, ", nodiratime");
54 if (flags & MS_BIND)
55 audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind");
56 if (flags & MS_MOVE)
57 audit_log_format(ab, ", move");
58 if (flags & MS_SILENT)
59 audit_log_format(ab, ", silent");
60 if (flags & MS_POSIXACL)
61 audit_log_format(ab, ", acl");
62 if (flags & MS_UNBINDABLE)
63 audit_log_format(ab, flags & MS_REC ? ", runbindable" :
64 ", unbindable");
65 if (flags & MS_PRIVATE)
66 audit_log_format(ab, flags & MS_REC ? ", rprivate" :
67 ", private");
68 if (flags & MS_SLAVE)
69 audit_log_format(ab, flags & MS_REC ? ", rslave" :
70 ", slave");
71 if (flags & MS_SHARED)
72 audit_log_format(ab, flags & MS_REC ? ", rshared" :
73 ", shared");
74 if (flags & MS_RELATIME)
75 audit_log_format(ab, ", relatime");
76 if (flags & MS_I_VERSION)
77 audit_log_format(ab, ", iversion");
78 if (flags & MS_STRICTATIME)
79 audit_log_format(ab, ", strictatime");
80 if (flags & MS_NOUSER)
81 audit_log_format(ab, ", nouser");
82}
83
84/**
85 * audit_cb - call back for mount specific audit fields
86 * @ab: audit_buffer (NOT NULL)
87 * @va: audit struct to audit values of (NOT NULL)
88 */
89static void audit_cb(struct audit_buffer *ab, void *va)
90{
91 struct common_audit_data *sa = va;
92
93 if (aad(sa)->mnt.type) {
94 audit_log_format(ab, " fstype=");
95 audit_log_untrustedstring(ab, aad(sa)->mnt.type);
96 }
97 if (aad(sa)->mnt.src_name) {
98 audit_log_format(ab, " srcname=");
99 audit_log_untrustedstring(ab, aad(sa)->mnt.src_name);
100 }
101 if (aad(sa)->mnt.trans) {
102 audit_log_format(ab, " trans=");
103 audit_log_untrustedstring(ab, aad(sa)->mnt.trans);
104 }
105 if (aad(sa)->mnt.flags) {
106 audit_log_format(ab, " flags=\"");
107 audit_mnt_flags(ab, aad(sa)->mnt.flags);
108 audit_log_format(ab, "\"");
109 }
110 if (aad(sa)->mnt.data) {
111 audit_log_format(ab, " options=");
112 audit_log_untrustedstring(ab, aad(sa)->mnt.data);
113 }
114}
115
116/**
117 * audit_mount - handle the auditing of mount operations
118 * @profile: the profile being enforced (NOT NULL)
119 * @op: operation being mediated (NOT NULL)
120 * @name: name of object being mediated (MAYBE NULL)
121 * @src_name: src_name of object being mediated (MAYBE_NULL)
122 * @type: type of filesystem (MAYBE_NULL)
123 * @trans: name of trans (MAYBE NULL)
124 * @flags: filesystem idependent mount flags
125 * @data: filesystem mount flags
126 * @request: permissions requested
127 * @perms: the permissions computed for the request (NOT NULL)
128 * @info: extra information message (MAYBE NULL)
129 * @error: 0 if operation allowed else failure error code
130 *
131 * Returns: %0 or error on failure
132 */
133static int audit_mount(struct aa_profile *profile, const char *op,
134 const char *name, const char *src_name,
135 const char *type, const char *trans,
136 unsigned long flags, const void *data, u32 request,
137 struct aa_perms *perms, const char *info, int error)
138{
139 int audit_type = AUDIT_APPARMOR_AUTO;
140 DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
141
142 if (likely(!error)) {
143 u32 mask = perms->audit;
144
145 if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
146 mask = 0xffff;
147
148 /* mask off perms that are not being force audited */
149 request &= mask;
150
151 if (likely(!request))
152 return 0;
153 audit_type = AUDIT_APPARMOR_AUDIT;
154 } else {
155 /* only report permissions that were denied */
156 request = request & ~perms->allow;
157
158 if (request & perms->kill)
159 audit_type = AUDIT_APPARMOR_KILL;
160
161 /* quiet known rejects, assumes quiet and kill do not overlap */
162 if ((request & perms->quiet) &&
163 AUDIT_MODE(profile) != AUDIT_NOQUIET &&
164 AUDIT_MODE(profile) != AUDIT_ALL)
165 request &= ~perms->quiet;
166
167 if (!request)
168 return error;
169 }
170
171 aad(&sa)->name = name;
172 aad(&sa)->mnt.src_name = src_name;
173 aad(&sa)->mnt.type = type;
174 aad(&sa)->mnt.trans = trans;
175 aad(&sa)->mnt.flags = flags;
176 if (data && (perms->audit & AA_AUDIT_DATA))
177 aad(&sa)->mnt.data = data;
178 aad(&sa)->info = info;
179 aad(&sa)->error = error;
180
181 return aa_audit(audit_type, profile, &sa, audit_cb);
182}
183
184/**
185 * match_mnt_flags - Do an ordered match on mount flags
186 * @dfa: dfa to match against
187 * @state: state to start in
188 * @flags: mount flags to match against
189 *
190 * Mount flags are encoded as an ordered match. This is done instead of
191 * checking against a simple bitmask, to allow for logical operations
192 * on the flags.
193 *
194 * Returns: next state after flags match
195 */
196static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
197 unsigned long flags)
198{
199 unsigned int i;
200
201 for (i = 0; i <= 31 ; ++i) {
202 if ((1 << i) & flags)
203 state = aa_dfa_next(dfa, state, i + 1);
204 }
205
206 return state;
207}
208
209/**
210 * compute_mnt_perms - compute mount permission associated with @state
211 * @dfa: dfa to match against (NOT NULL)
212 * @state: state match finished in
213 *
214 * Returns: mount permissions
215 */
216static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa,
217 unsigned int state)
218{
219 struct aa_perms perms;
220
221 perms.kill = 0;
222 perms.allow = dfa_user_allow(dfa, state);
223 perms.audit = dfa_user_audit(dfa, state);
224 perms.quiet = dfa_user_quiet(dfa, state);
225 perms.xindex = dfa_user_xindex(dfa, state);
226
227 return perms;
228}
229
230static const char * const mnt_info_table[] = {
231 "match succeeded",
232 "failed mntpnt match",
233 "failed srcname match",
234 "failed type match",
235 "failed flags match",
236 "failed data match"
237};
238
239/*
240 * Returns 0 on success else element that match failed in, this is the
241 * index into the mnt_info_table above
242 */
243static int do_match_mnt(struct aa_dfa *dfa, unsigned int start,
244 const char *mntpnt, const char *devname,
245 const char *type, unsigned long flags,
246 void *data, bool binary, struct aa_perms *perms)
247{
248 unsigned int state;
249
250 AA_BUG(!dfa);
251 AA_BUG(!perms);
252
253 state = aa_dfa_match(dfa, start, mntpnt);
254 state = aa_dfa_null_transition(dfa, state);
255 if (!state)
256 return 1;
257
258 if (devname)
259 state = aa_dfa_match(dfa, state, devname);
260 state = aa_dfa_null_transition(dfa, state);
261 if (!state)
262 return 2;
263
264 if (type)
265 state = aa_dfa_match(dfa, state, type);
266 state = aa_dfa_null_transition(dfa, state);
267 if (!state)
268 return 3;
269
270 state = match_mnt_flags(dfa, state, flags);
271 if (!state)
272 return 4;
273 *perms = compute_mnt_perms(dfa, state);
274 if (perms->allow & AA_MAY_MOUNT)
275 return 0;
276
277 /* only match data if not binary and the DFA flags data is expected */
278 if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) {
279 state = aa_dfa_null_transition(dfa, state);
280 if (!state)
281 return 4;
282
283 state = aa_dfa_match(dfa, state, data);
284 if (!state)
285 return 5;
286 *perms = compute_mnt_perms(dfa, state);
287 if (perms->allow & AA_MAY_MOUNT)
288 return 0;
289 }
290
291 /* failed at end of flags match */
292 return 4;
293}
294
295
296static int path_flags(struct aa_profile *profile, const struct path *path)
297{
298 AA_BUG(!profile);
299 AA_BUG(!path);
300
301 return profile->path_flags |
302 (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0);
303}
304
305/**
306 * match_mnt_path_str - handle path matching for mount
307 * @profile: the confining profile
308 * @mntpath: for the mntpnt (NOT NULL)
309 * @buffer: buffer to be used to lookup mntpath
310 * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR)
311 * @type: string for the dev type (MAYBE NULL)
312 * @flags: mount flags to match
313 * @data: fs mount data (MAYBE NULL)
314 * @binary: whether @data is binary
315 * @devinfo: error str if (IS_ERR(@devname))
316 *
317 * Returns: 0 on success else error
318 */
319static int match_mnt_path_str(struct aa_profile *profile,
320 const struct path *mntpath, char *buffer,
321 const char *devname, const char *type,
322 unsigned long flags, void *data, bool binary,
323 const char *devinfo)
324{
325 struct aa_perms perms = { };
326 const char *mntpnt = NULL, *info = NULL;
327 int pos, error;
328
329 AA_BUG(!profile);
330 AA_BUG(!mntpath);
331 AA_BUG(!buffer);
332
333 error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
334 &mntpnt, &info, profile->disconnected);
335 if (error)
336 goto audit;
337 if (IS_ERR(devname)) {
338 error = PTR_ERR(devname);
339 devname = NULL;
340 info = devinfo;
341 goto audit;
342 }
343
344 error = -EACCES;
345 pos = do_match_mnt(profile->policy.dfa,
346 profile->policy.start[AA_CLASS_MOUNT],
347 mntpnt, devname, type, flags, data, binary, &perms);
348 if (pos) {
349 info = mnt_info_table[pos];
350 goto audit;
351 }
352 error = 0;
353
354audit:
355 return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL,
356 flags, data, AA_MAY_MOUNT, &perms, info, error);
357}
358
359/**
360 * match_mnt - handle path matching for mount
361 * @profile: the confining profile
362 * @mntpath: for the mntpnt (NOT NULL)
363 * @buffer: buffer to be used to lookup mntpath
364 * @devpath: path devname/src_name (MAYBE NULL)
365 * @devbuffer: buffer to be used to lookup devname/src_name
366 * @type: string for the dev type (MAYBE NULL)
367 * @flags: mount flags to match
368 * @data: fs mount data (MAYBE NULL)
369 * @binary: whether @data is binary
370 *
371 * Returns: 0 on success else error
372 */
373static int match_mnt(struct aa_profile *profile, const struct path *path,
374 char *buffer, struct path *devpath, char *devbuffer,
375 const char *type, unsigned long flags, void *data,
376 bool binary)
377{
378 const char *devname = NULL, *info = NULL;
379 int error = -EACCES;
380
381 AA_BUG(!profile);
382 AA_BUG(devpath && !devbuffer);
383
384 if (devpath) {
385 error = aa_path_name(devpath, path_flags(profile, devpath),
386 devbuffer, &devname, &info,
387 profile->disconnected);
388 if (error)
389 devname = ERR_PTR(error);
390 }
391
392 return match_mnt_path_str(profile, path, buffer, devname, type, flags,
393 data, binary, info);
394}
395
396int aa_remount(struct aa_label *label, const struct path *path,
397 unsigned long flags, void *data)
398{
399 struct aa_profile *profile;
400 char *buffer = NULL;
401 bool binary;
402 int error;
403
404 AA_BUG(!label);
405 AA_BUG(!path);
406
407 binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
408
409 get_buffers(buffer);
410 error = fn_for_each_confined(label, profile,
411 match_mnt(profile, path, buffer, NULL, NULL, NULL,
412 flags, data, binary));
413 put_buffers(buffer);
414
415 return error;
416}
417
418int aa_bind_mount(struct aa_label *label, const struct path *path,
419 const char *dev_name, unsigned long flags)
420{
421 struct aa_profile *profile;
422 char *buffer = NULL, *old_buffer = NULL;
423 struct path old_path;
424 int error;
425
426 AA_BUG(!label);
427 AA_BUG(!path);
428
429 if (!dev_name || !*dev_name)
430 return -EINVAL;
431
432 flags &= MS_REC | MS_BIND;
433
434 error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
435 if (error)
436 return error;
437
438 get_buffers(buffer, old_buffer);
439 error = fn_for_each_confined(label, profile,
440 match_mnt(profile, path, buffer, &old_path, old_buffer,
441 NULL, flags, NULL, false));
442 put_buffers(buffer, old_buffer);
443 path_put(&old_path);
444
445 return error;
446}
447
448int aa_mount_change_type(struct aa_label *label, const struct path *path,
449 unsigned long flags)
450{
451 struct aa_profile *profile;
452 char *buffer = NULL;
453 int error;
454
455 AA_BUG(!label);
456 AA_BUG(!path);
457
458 /* These are the flags allowed by do_change_type() */
459 flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
460 MS_UNBINDABLE);
461
462 get_buffers(buffer);
463 error = fn_for_each_confined(label, profile,
464 match_mnt(profile, path, buffer, NULL, NULL, NULL,
465 flags, NULL, false));
466 put_buffers(buffer);
467
468 return error;
469}
470
471int aa_move_mount(struct aa_label *label, const struct path *path,
472 const char *orig_name)
473{
474 struct aa_profile *profile;
475 char *buffer = NULL, *old_buffer = NULL;
476 struct path old_path;
477 int error;
478
479 AA_BUG(!label);
480 AA_BUG(!path);
481
482 if (!orig_name || !*orig_name)
483 return -EINVAL;
484
485 error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
486 if (error)
487 return error;
488
489 get_buffers(buffer, old_buffer);
490 error = fn_for_each_confined(label, profile,
491 match_mnt(profile, path, buffer, &old_path, old_buffer,
492 NULL, MS_MOVE, NULL, false));
493 put_buffers(buffer, old_buffer);
494 path_put(&old_path);
495
496 return error;
497}
498
499int aa_new_mount(struct aa_label *label, const char *dev_name,
500 const struct path *path, const char *type, unsigned long flags,
501 void *data)
502{
503 struct aa_profile *profile;
504 char *buffer = NULL, *dev_buffer = NULL;
505 bool binary = true;
506 int error;
507 int requires_dev = 0;
508 struct path tmp_path, *dev_path = NULL;
509
510 AA_BUG(!label);
511 AA_BUG(!path);
512
513 if (type) {
514 struct file_system_type *fstype;
515
516 fstype = get_fs_type(type);
517 if (!fstype)
518 return -ENODEV;
519 binary = fstype->fs_flags & FS_BINARY_MOUNTDATA;
520 requires_dev = fstype->fs_flags & FS_REQUIRES_DEV;
521 put_filesystem(fstype);
522
523 if (requires_dev) {
524 if (!dev_name || !*dev_name)
525 return -ENOENT;
526
527 error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path);
528 if (error)
529 return error;
530 dev_path = &tmp_path;
531 }
532 }
533
534 get_buffers(buffer, dev_buffer);
535 if (dev_path) {
536 error = fn_for_each_confined(label, profile,
537 match_mnt(profile, path, buffer, dev_path, dev_buffer,
538 type, flags, data, binary));
539 } else {
540 error = fn_for_each_confined(label, profile,
541 match_mnt_path_str(profile, path, buffer, dev_name,
542 type, flags, data, binary, NULL));
543 }
544 put_buffers(buffer, dev_buffer);
545 if (dev_path)
546 path_put(dev_path);
547
548 return error;
549}
550
551static int profile_umount(struct aa_profile *profile, struct path *path,
552 char *buffer)
553{
554 struct aa_perms perms = { };
555 const char *name = NULL, *info = NULL;
556 unsigned int state;
557 int error;
558
559 AA_BUG(!profile);
560 AA_BUG(!path);
561
562 error = aa_path_name(path, path_flags(profile, path), buffer, &name,
563 &info, profile->disconnected);
564 if (error)
565 goto audit;
566
567 state = aa_dfa_match(profile->policy.dfa,
568 profile->policy.start[AA_CLASS_MOUNT],
569 name);
570 perms = compute_mnt_perms(profile->policy.dfa, state);
571 if (AA_MAY_UMOUNT & ~perms.allow)
572 error = -EACCES;
573
574audit:
575 return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL,
576 AA_MAY_UMOUNT, &perms, info, error);
577}
578
579int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags)
580{
581 struct aa_profile *profile;
582 char *buffer = NULL;
583 int error;
584 struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
585
586 AA_BUG(!label);
587 AA_BUG(!mnt);
588
589 get_buffers(buffer);
590 error = fn_for_each_confined(label, profile,
591 profile_umount(profile, &path, buffer));
592 put_buffers(buffer);
593
594 return error;
595}
596
597/* helper fn for transition on pivotroot
598 *
599 * Returns: label for transition or ERR_PTR. Does not return NULL
600 */
601static struct aa_label *build_pivotroot(struct aa_profile *profile,
602 const struct path *new_path,
603 char *new_buffer,
604 const struct path *old_path,
605 char *old_buffer)
606{
607 const char *old_name, *new_name = NULL, *info = NULL;
608 const char *trans_name = NULL;
609 struct aa_perms perms = { };
610 unsigned int state;
611 int error;
612
613 AA_BUG(!profile);
614 AA_BUG(!new_path);
615 AA_BUG(!old_path);
616
617 if (profile_unconfined(profile))
618 return aa_get_newest_label(&profile->label);
619
620 error = aa_path_name(old_path, path_flags(profile, old_path),
621 old_buffer, &old_name, &info,
622 profile->disconnected);
623 if (error)
624 goto audit;
625 error = aa_path_name(new_path, path_flags(profile, new_path),
626 new_buffer, &new_name, &info,
627 profile->disconnected);
628 if (error)
629 goto audit;
630
631 error = -EACCES;
632 state = aa_dfa_match(profile->policy.dfa,
633 profile->policy.start[AA_CLASS_MOUNT],
634 new_name);
635 state = aa_dfa_null_transition(profile->policy.dfa, state);
636 state = aa_dfa_match(profile->policy.dfa, state, old_name);
637 perms = compute_mnt_perms(profile->policy.dfa, state);
638
639 if (AA_MAY_PIVOTROOT & perms.allow)
640 error = 0;
641
642audit:
643 error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name,
644 NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT,
645 &perms, info, error);
646 if (error)
647 return ERR_PTR(error);
648
649 return aa_get_newest_label(&profile->label);
650}
651
652int aa_pivotroot(struct aa_label *label, const struct path *old_path,
653 const struct path *new_path)
654{
655 struct aa_profile *profile;
656 struct aa_label *target = NULL;
657 char *old_buffer = NULL, *new_buffer = NULL, *info = NULL;
658 int error;
659
660 AA_BUG(!label);
661 AA_BUG(!old_path);
662 AA_BUG(!new_path);
663
664 get_buffers(old_buffer, new_buffer);
665 target = fn_label_build(label, profile, GFP_ATOMIC,
666 build_pivotroot(profile, new_path, new_buffer,
667 old_path, old_buffer));
668 if (!target) {
669 info = "label build failed";
670 error = -ENOMEM;
671 goto fail;
672 } else if (!IS_ERR(target)) {
673 error = aa_replace_current_label(target);
674 if (error) {
675 /* TODO: audit target */
676 aa_put_label(target);
677 goto out;
678 }
679 } else
680 /* already audited error */
681 error = PTR_ERR(target);
682out:
683 put_buffers(old_buffer, new_buffer);
684
685 return error;
686
687fail:
688 /* TODO: add back in auditing of new_name and old_name */
689 error = fn_for_each(label, profile,
690 audit_mount(profile, OP_PIVOTROOT, NULL /*new_name */,
691 NULL /* old_name */,
692 NULL, NULL,
693 0, NULL, AA_MAY_PIVOTROOT, &nullperms, info,
694 error));
695 goto out;
696}