Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+841 -4
+1 -1
security/apparmor/Makefile
··· 4 4 5 5 apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ 6 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 8 8 apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o 9 9 10 10 clean-files := capability_names.h rlim_names.h
+7 -1
security/apparmor/apparmorfs.c
··· 2159 2159 { } 2160 2160 }; 2161 2161 2162 + static struct aa_sfs_entry aa_sfs_entry_mount[] = { 2163 + AA_SFS_FILE_STRING("mask", "mount umount pivot_root"), 2164 + { } 2165 + }; 2166 + 2162 2167 static struct aa_sfs_entry aa_sfs_entry_ns[] = { 2163 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 ··· 2185 2180 AA_SFS_DIR("policy", aa_sfs_entry_policy), 2186 2181 AA_SFS_DIR("domain", aa_sfs_entry_domain), 2187 2182 AA_SFS_DIR("file", aa_sfs_entry_file), 2183 + AA_SFS_DIR("mount", aa_sfs_entry_mount), 2188 2184 AA_SFS_DIR("namespaces", aa_sfs_entry_ns), 2189 2185 AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), 2190 2186 AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit),
+2 -2
security/apparmor/domain.c
··· 374 374 * 375 375 * Returns: refcounted label, or NULL on failure (MAYBE NULL) 376 376 */ 377 - static struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, 378 - const char **name) 377 + struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, 378 + const char **name) 379 379 { 380 380 struct aa_label *label = NULL; 381 381 u32 xtype = xindex & AA_X_TYPE_MASK;
+1
security/apparmor/include/apparmor.h
··· 27 27 #define AA_CLASS_NET 4 28 28 #define AA_CLASS_RLIMITS 5 29 29 #define AA_CLASS_DOMAIN 6 30 + #define AA_CLASS_MOUNT 7 30 31 #define AA_CLASS_PTRACE 9 31 32 #define AA_CLASS_SIGNAL 10 32 33 #define AA_CLASS_LABEL 16
+11
security/apparmor/include/audit.h
··· 71 71 #define OP_FMPROT "file_mprotect" 72 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 78 #define OP_CREATE "create" 75 79 #define OP_POST_CREATE "post_create" 76 80 #define OP_BIND "bind" ··· 136 132 int rlim; 137 133 unsigned long max; 138 134 } rlim; 135 + struct { 136 + const char *src_name; 137 + const char *type; 138 + const char *trans; 139 + const char *data; 140 + unsigned long flags; 141 + } mnt; 139 142 }; 140 143 }; 141 144
+5
security/apparmor/include/domain.h
··· 15 15 #include <linux/binfmts.h> 16 16 #include <linux/types.h> 17 17 18 + #include "label.h" 19 + 18 20 #ifndef __AA_DOMAIN_H 19 21 #define __AA_DOMAIN_H 20 22 ··· 30 28 #define AA_CHANGE_CHILD 2 31 29 #define AA_CHANGE_ONEXEC 4 32 30 #define AA_CHANGE_STACK 8 31 + 32 + struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, 33 + const char **name); 33 34 34 35 int apparmor_bprm_set_creds(struct linux_binprm *bprm); 35 36 int apparmor_bprm_secureexec(struct linux_binprm *bprm);
+54
security/apparmor/include/mount.h
··· 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 + 32 + int aa_remount(struct aa_label *label, const struct path *path, 33 + unsigned long flags, void *data); 34 + 35 + int aa_bind_mount(struct aa_label *label, const struct path *path, 36 + const char *old_name, unsigned long flags); 37 + 38 + 39 + int aa_mount_change_type(struct aa_label *label, const struct path *path, 40 + unsigned long flags); 41 + 42 + int aa_move_mount(struct aa_label *label, const struct path *path, 43 + const char *old_name); 44 + 45 + int 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 + 49 + int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags); 50 + 51 + int aa_pivotroot(struct aa_label *label, const struct path *old_path, 52 + const struct path *new_path); 53 + 54 + #endif /* __AA_MOUNT_H */
+64
security/apparmor/lsm.c
··· 38 38 #include "include/policy.h" 39 39 #include "include/policy_ns.h" 40 40 #include "include/procattr.h" 41 + #include "include/mount.h" 41 42 42 43 /* Flag indicating whether initialization completed */ 43 44 int apparmor_initialized; ··· 512 511 !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); 513 512 } 514 513 514 + static int apparmor_sb_mount(const char *dev_name, const struct path *path, 515 + const char *type, unsigned long flags, void *data) 516 + { 517 + struct aa_label *label; 518 + int error = 0; 519 + 520 + /* Discard magic */ 521 + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) 522 + flags &= ~MS_MGC_MSK; 523 + 524 + flags &= ~AA_MS_IGNORE_MASK; 525 + 526 + label = __begin_current_label_crit_section(); 527 + if (!unconfined(label)) { 528 + if (flags & MS_REMOUNT) 529 + error = aa_remount(label, path, flags, data); 530 + else if (flags & MS_BIND) 531 + error = aa_bind_mount(label, path, dev_name, flags); 532 + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | 533 + MS_UNBINDABLE)) 534 + error = aa_mount_change_type(label, path, flags); 535 + else if (flags & MS_MOVE) 536 + error = aa_move_mount(label, path, dev_name); 537 + else 538 + error = aa_new_mount(label, dev_name, path, type, 539 + flags, data); 540 + } 541 + __end_current_label_crit_section(label); 542 + 543 + return error; 544 + } 545 + 546 + static int apparmor_sb_umount(struct vfsmount *mnt, int flags) 547 + { 548 + struct aa_label *label; 549 + int error = 0; 550 + 551 + label = __begin_current_label_crit_section(); 552 + if (!unconfined(label)) 553 + error = aa_umount(label, mnt, flags); 554 + __end_current_label_crit_section(label); 555 + 556 + return error; 557 + } 558 + 559 + static int apparmor_sb_pivotroot(const struct path *old_path, 560 + const struct path *new_path) 561 + { 562 + struct aa_label *label; 563 + int error = 0; 564 + 565 + label = aa_get_current_label(); 566 + if (!unconfined(label)) 567 + error = aa_pivotroot(label, old_path, new_path); 568 + aa_put_label(label); 569 + 570 + return error; 571 + } 572 + 515 573 static int apparmor_getprocattr(struct task_struct *task, char *name, 516 574 char **value) 517 575 { ··· 741 681 LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), 742 682 LSM_HOOK_INIT(capget, apparmor_capget), 743 683 LSM_HOOK_INIT(capable, apparmor_capable), 684 + 685 + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), 686 + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), 687 + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), 744 688 745 689 LSM_HOOK_INIT(path_link, apparmor_path_link), 746 690 LSM_HOOK_INIT(path_unlink, apparmor_path_unlink),
+696
security/apparmor/mount.c
··· 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 + 30 + static 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 + */ 89 + static 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 + */ 133 + static 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 + */ 196 + static 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 + */ 216 + static 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 + 230 + static 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 + */ 243 + static 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 + 296 + static 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 + */ 319 + static 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 + 354 + audit: 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 + */ 373 + static 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 + 396 + int 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 + 418 + int 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 + 448 + int 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 + 471 + int 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 + 499 + int 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 + 551 + static 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 + 574 + audit: 575 + return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL, 576 + AA_MAY_UMOUNT, &perms, info, error); 577 + } 578 + 579 + int 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 + */ 601 + static 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 + 642 + audit: 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 + 652 + int 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); 682 + out: 683 + put_buffers(old_buffer, new_buffer); 684 + 685 + return error; 686 + 687 + fail: 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 + }