1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22ea3ffb7SJohn Johansen /*
32ea3ffb7SJohn Johansen * AppArmor security module
42ea3ffb7SJohn Johansen *
52ea3ffb7SJohn Johansen * This file contains AppArmor mediation of files
62ea3ffb7SJohn Johansen *
72ea3ffb7SJohn Johansen * Copyright (C) 1998-2008 Novell/SUSE
82ea3ffb7SJohn Johansen * Copyright 2009-2017 Canonical Ltd.
92ea3ffb7SJohn Johansen */
102ea3ffb7SJohn Johansen
112ea3ffb7SJohn Johansen #include <linux/fs.h>
122ea3ffb7SJohn Johansen #include <linux/mount.h>
132ea3ffb7SJohn Johansen #include <linux/namei.h>
14e262e32dSDavid Howells #include <uapi/linux/mount.h>
152ea3ffb7SJohn Johansen
162ea3ffb7SJohn Johansen #include "include/apparmor.h"
172ea3ffb7SJohn Johansen #include "include/audit.h"
18d8889d49SJohn Johansen #include "include/cred.h"
192ea3ffb7SJohn Johansen #include "include/domain.h"
202ea3ffb7SJohn Johansen #include "include/file.h"
212ea3ffb7SJohn Johansen #include "include/match.h"
222ea3ffb7SJohn Johansen #include "include/mount.h"
232ea3ffb7SJohn Johansen #include "include/path.h"
242ea3ffb7SJohn Johansen #include "include/policy.h"
252ea3ffb7SJohn Johansen
262ea3ffb7SJohn Johansen
audit_mnt_flags(struct audit_buffer * ab,unsigned long flags)272ea3ffb7SJohn Johansen static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
282ea3ffb7SJohn Johansen {
292ea3ffb7SJohn Johansen if (flags & MS_RDONLY)
302ea3ffb7SJohn Johansen audit_log_format(ab, "ro");
312ea3ffb7SJohn Johansen else
322ea3ffb7SJohn Johansen audit_log_format(ab, "rw");
332ea3ffb7SJohn Johansen if (flags & MS_NOSUID)
342ea3ffb7SJohn Johansen audit_log_format(ab, ", nosuid");
352ea3ffb7SJohn Johansen if (flags & MS_NODEV)
362ea3ffb7SJohn Johansen audit_log_format(ab, ", nodev");
372ea3ffb7SJohn Johansen if (flags & MS_NOEXEC)
382ea3ffb7SJohn Johansen audit_log_format(ab, ", noexec");
392ea3ffb7SJohn Johansen if (flags & MS_SYNCHRONOUS)
402ea3ffb7SJohn Johansen audit_log_format(ab, ", sync");
412ea3ffb7SJohn Johansen if (flags & MS_REMOUNT)
422ea3ffb7SJohn Johansen audit_log_format(ab, ", remount");
432ea3ffb7SJohn Johansen if (flags & MS_MANDLOCK)
442ea3ffb7SJohn Johansen audit_log_format(ab, ", mand");
452ea3ffb7SJohn Johansen if (flags & MS_DIRSYNC)
462ea3ffb7SJohn Johansen audit_log_format(ab, ", dirsync");
472ea3ffb7SJohn Johansen if (flags & MS_NOATIME)
482ea3ffb7SJohn Johansen audit_log_format(ab, ", noatime");
492ea3ffb7SJohn Johansen if (flags & MS_NODIRATIME)
502ea3ffb7SJohn Johansen audit_log_format(ab, ", nodiratime");
512ea3ffb7SJohn Johansen if (flags & MS_BIND)
522ea3ffb7SJohn Johansen audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind");
532ea3ffb7SJohn Johansen if (flags & MS_MOVE)
542ea3ffb7SJohn Johansen audit_log_format(ab, ", move");
552ea3ffb7SJohn Johansen if (flags & MS_SILENT)
562ea3ffb7SJohn Johansen audit_log_format(ab, ", silent");
572ea3ffb7SJohn Johansen if (flags & MS_POSIXACL)
582ea3ffb7SJohn Johansen audit_log_format(ab, ", acl");
592ea3ffb7SJohn Johansen if (flags & MS_UNBINDABLE)
602ea3ffb7SJohn Johansen audit_log_format(ab, flags & MS_REC ? ", runbindable" :
612ea3ffb7SJohn Johansen ", unbindable");
622ea3ffb7SJohn Johansen if (flags & MS_PRIVATE)
632ea3ffb7SJohn Johansen audit_log_format(ab, flags & MS_REC ? ", rprivate" :
642ea3ffb7SJohn Johansen ", private");
652ea3ffb7SJohn Johansen if (flags & MS_SLAVE)
662ea3ffb7SJohn Johansen audit_log_format(ab, flags & MS_REC ? ", rslave" :
672ea3ffb7SJohn Johansen ", slave");
682ea3ffb7SJohn Johansen if (flags & MS_SHARED)
692ea3ffb7SJohn Johansen audit_log_format(ab, flags & MS_REC ? ", rshared" :
702ea3ffb7SJohn Johansen ", shared");
712ea3ffb7SJohn Johansen if (flags & MS_RELATIME)
722ea3ffb7SJohn Johansen audit_log_format(ab, ", relatime");
732ea3ffb7SJohn Johansen if (flags & MS_I_VERSION)
742ea3ffb7SJohn Johansen audit_log_format(ab, ", iversion");
752ea3ffb7SJohn Johansen if (flags & MS_STRICTATIME)
762ea3ffb7SJohn Johansen audit_log_format(ab, ", strictatime");
772ea3ffb7SJohn Johansen if (flags & MS_NOUSER)
782ea3ffb7SJohn Johansen audit_log_format(ab, ", nouser");
792ea3ffb7SJohn Johansen }
802ea3ffb7SJohn Johansen
812ea3ffb7SJohn Johansen /**
822ea3ffb7SJohn Johansen * audit_cb - call back for mount specific audit fields
832ea3ffb7SJohn Johansen * @ab: audit_buffer (NOT NULL)
842ea3ffb7SJohn Johansen * @va: audit struct to audit values of (NOT NULL)
852ea3ffb7SJohn Johansen */
audit_cb(struct audit_buffer * ab,void * va)862ea3ffb7SJohn Johansen static void audit_cb(struct audit_buffer *ab, void *va)
872ea3ffb7SJohn Johansen {
882ea3ffb7SJohn Johansen struct common_audit_data *sa = va;
89c57bc80fSJohn Johansen struct apparmor_audit_data *ad = aad(sa);
902ea3ffb7SJohn Johansen
91c57bc80fSJohn Johansen if (ad->mnt.type) {
922ea3ffb7SJohn Johansen audit_log_format(ab, " fstype=");
93c57bc80fSJohn Johansen audit_log_untrustedstring(ab, ad->mnt.type);
942ea3ffb7SJohn Johansen }
95c57bc80fSJohn Johansen if (ad->mnt.src_name) {
962ea3ffb7SJohn Johansen audit_log_format(ab, " srcname=");
97c57bc80fSJohn Johansen audit_log_untrustedstring(ab, ad->mnt.src_name);
982ea3ffb7SJohn Johansen }
99c57bc80fSJohn Johansen if (ad->mnt.trans) {
1002ea3ffb7SJohn Johansen audit_log_format(ab, " trans=");
101c57bc80fSJohn Johansen audit_log_untrustedstring(ab, ad->mnt.trans);
1022ea3ffb7SJohn Johansen }
103c57bc80fSJohn Johansen if (ad->mnt.flags) {
1042ea3ffb7SJohn Johansen audit_log_format(ab, " flags=\"");
105c57bc80fSJohn Johansen audit_mnt_flags(ab, ad->mnt.flags);
1062ea3ffb7SJohn Johansen audit_log_format(ab, "\"");
1072ea3ffb7SJohn Johansen }
108c57bc80fSJohn Johansen if (ad->mnt.data) {
1092ea3ffb7SJohn Johansen audit_log_format(ab, " options=");
110c57bc80fSJohn Johansen audit_log_untrustedstring(ab, ad->mnt.data);
1112ea3ffb7SJohn Johansen }
1122ea3ffb7SJohn Johansen }
1132ea3ffb7SJohn Johansen
1142ea3ffb7SJohn Johansen /**
1152ea3ffb7SJohn Johansen * audit_mount - handle the auditing of mount operations
116690f33e1SJohn Johansen * @subj_cred: cred of the subject
1172ea3ffb7SJohn Johansen * @profile: the profile being enforced (NOT NULL)
1182ea3ffb7SJohn Johansen * @op: operation being mediated (NOT NULL)
1192ea3ffb7SJohn Johansen * @name: name of object being mediated (MAYBE NULL)
1202ea3ffb7SJohn Johansen * @src_name: src_name of object being mediated (MAYBE_NULL)
1212ea3ffb7SJohn Johansen * @type: type of filesystem (MAYBE_NULL)
1222ea3ffb7SJohn Johansen * @trans: name of trans (MAYBE NULL)
12368a1a0c6SZygmunt Krynicki * @flags: filesystem independent mount flags
1242ea3ffb7SJohn Johansen * @data: filesystem mount flags
1252ea3ffb7SJohn Johansen * @request: permissions requested
1262ea3ffb7SJohn Johansen * @perms: the permissions computed for the request (NOT NULL)
1272ea3ffb7SJohn Johansen * @info: extra information message (MAYBE NULL)
1282ea3ffb7SJohn Johansen * @error: 0 if operation allowed else failure error code
1292ea3ffb7SJohn Johansen *
1302ea3ffb7SJohn Johansen * Returns: %0 or error on failure
1312ea3ffb7SJohn Johansen */
audit_mount(const struct cred * subj_cred,struct aa_profile * profile,const char * op,const char * name,const char * src_name,const char * type,const char * trans,unsigned long flags,const void * data,u32 request,struct aa_perms * perms,const char * info,int error)132690f33e1SJohn Johansen static int audit_mount(const struct cred *subj_cred,
133690f33e1SJohn Johansen struct aa_profile *profile, const char *op,
1342ea3ffb7SJohn Johansen const char *name, const char *src_name,
1352ea3ffb7SJohn Johansen const char *type, const char *trans,
1362ea3ffb7SJohn Johansen unsigned long flags, const void *data, u32 request,
1372ea3ffb7SJohn Johansen struct aa_perms *perms, const char *info, int error)
1382ea3ffb7SJohn Johansen {
1392ea3ffb7SJohn Johansen int audit_type = AUDIT_APPARMOR_AUTO;
140c57bc80fSJohn Johansen DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_MOUNT, op);
1412ea3ffb7SJohn Johansen
1422ea3ffb7SJohn Johansen if (likely(!error)) {
1432ea3ffb7SJohn Johansen u32 mask = perms->audit;
1442ea3ffb7SJohn Johansen
1452ea3ffb7SJohn Johansen if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
1462ea3ffb7SJohn Johansen mask = 0xffff;
1472ea3ffb7SJohn Johansen
1482ea3ffb7SJohn Johansen /* mask off perms that are not being force audited */
1492ea3ffb7SJohn Johansen request &= mask;
1502ea3ffb7SJohn Johansen
1512ea3ffb7SJohn Johansen if (likely(!request))
1522ea3ffb7SJohn Johansen return 0;
1532ea3ffb7SJohn Johansen audit_type = AUDIT_APPARMOR_AUDIT;
1542ea3ffb7SJohn Johansen } else {
1552ea3ffb7SJohn Johansen /* only report permissions that were denied */
1562ea3ffb7SJohn Johansen request = request & ~perms->allow;
1572ea3ffb7SJohn Johansen
1582ea3ffb7SJohn Johansen if (request & perms->kill)
1592ea3ffb7SJohn Johansen audit_type = AUDIT_APPARMOR_KILL;
1602ea3ffb7SJohn Johansen
1612ea3ffb7SJohn Johansen /* quiet known rejects, assumes quiet and kill do not overlap */
1622ea3ffb7SJohn Johansen if ((request & perms->quiet) &&
1632ea3ffb7SJohn Johansen AUDIT_MODE(profile) != AUDIT_NOQUIET &&
1642ea3ffb7SJohn Johansen AUDIT_MODE(profile) != AUDIT_ALL)
1652ea3ffb7SJohn Johansen request &= ~perms->quiet;
1662ea3ffb7SJohn Johansen
1672ea3ffb7SJohn Johansen if (!request)
1682ea3ffb7SJohn Johansen return error;
1692ea3ffb7SJohn Johansen }
1702ea3ffb7SJohn Johansen
171690f33e1SJohn Johansen ad.subj_cred = subj_cred;
172c57bc80fSJohn Johansen ad.name = name;
173c57bc80fSJohn Johansen ad.mnt.src_name = src_name;
174c57bc80fSJohn Johansen ad.mnt.type = type;
175c57bc80fSJohn Johansen ad.mnt.trans = trans;
176c57bc80fSJohn Johansen ad.mnt.flags = flags;
1772ea3ffb7SJohn Johansen if (data && (perms->audit & AA_AUDIT_DATA))
178c57bc80fSJohn Johansen ad.mnt.data = data;
179c57bc80fSJohn Johansen ad.info = info;
180c57bc80fSJohn Johansen ad.error = error;
1812ea3ffb7SJohn Johansen
182c57bc80fSJohn Johansen return aa_audit(audit_type, profile, &ad, audit_cb);
1832ea3ffb7SJohn Johansen }
1842ea3ffb7SJohn Johansen
1852ea3ffb7SJohn Johansen /**
1862ea3ffb7SJohn Johansen * match_mnt_flags - Do an ordered match on mount flags
1872ea3ffb7SJohn Johansen * @dfa: dfa to match against
1882ea3ffb7SJohn Johansen * @state: state to start in
1892ea3ffb7SJohn Johansen * @flags: mount flags to match against
1902ea3ffb7SJohn Johansen *
1912ea3ffb7SJohn Johansen * Mount flags are encoded as an ordered match. This is done instead of
1922ea3ffb7SJohn Johansen * checking against a simple bitmask, to allow for logical operations
1932ea3ffb7SJohn Johansen * on the flags.
1942ea3ffb7SJohn Johansen *
1952ea3ffb7SJohn Johansen * Returns: next state after flags match
1962ea3ffb7SJohn Johansen */
match_mnt_flags(struct aa_dfa * dfa,aa_state_t state,unsigned long flags)19733fc95d8SJohn Johansen static aa_state_t match_mnt_flags(struct aa_dfa *dfa, aa_state_t state,
1982ea3ffb7SJohn Johansen unsigned long flags)
1992ea3ffb7SJohn Johansen {
2002ea3ffb7SJohn Johansen unsigned int i;
2012ea3ffb7SJohn Johansen
2022ea3ffb7SJohn Johansen for (i = 0; i <= 31 ; ++i) {
2032ea3ffb7SJohn Johansen if ((1 << i) & flags)
2042ea3ffb7SJohn Johansen state = aa_dfa_next(dfa, state, i + 1);
2052ea3ffb7SJohn Johansen }
2062ea3ffb7SJohn Johansen
2072ea3ffb7SJohn Johansen return state;
2082ea3ffb7SJohn Johansen }
2092ea3ffb7SJohn Johansen
2102ea3ffb7SJohn Johansen static const char * const mnt_info_table[] = {
2112ea3ffb7SJohn Johansen "match succeeded",
2122ea3ffb7SJohn Johansen "failed mntpnt match",
2132ea3ffb7SJohn Johansen "failed srcname match",
2142ea3ffb7SJohn Johansen "failed type match",
2152ea3ffb7SJohn Johansen "failed flags match",
216ec240b59SJohn Johansen "failed data match",
217ec240b59SJohn Johansen "failed perms check"
2182ea3ffb7SJohn Johansen };
2192ea3ffb7SJohn Johansen
2202ea3ffb7SJohn Johansen /*
2212ea3ffb7SJohn Johansen * Returns 0 on success else element that match failed in, this is the
2222ea3ffb7SJohn Johansen * index into the mnt_info_table above
2232ea3ffb7SJohn Johansen */
do_match_mnt(struct aa_policydb * policy,aa_state_t start,const char * mntpnt,const char * devname,const char * type,unsigned long flags,void * data,bool binary,struct aa_perms * perms)22433fc95d8SJohn Johansen static int do_match_mnt(struct aa_policydb *policy, aa_state_t start,
2252ea3ffb7SJohn Johansen const char *mntpnt, const char *devname,
2262ea3ffb7SJohn Johansen const char *type, unsigned long flags,
2272ea3ffb7SJohn Johansen void *data, bool binary, struct aa_perms *perms)
2282ea3ffb7SJohn Johansen {
22933fc95d8SJohn Johansen aa_state_t state;
2302ea3ffb7SJohn Johansen
231e2967edeSJohn Johansen AA_BUG(!policy);
232e2967edeSJohn Johansen AA_BUG(!policy->dfa);
233e2967edeSJohn Johansen AA_BUG(!policy->perms);
2342ea3ffb7SJohn Johansen AA_BUG(!perms);
2352ea3ffb7SJohn Johansen
236e2967edeSJohn Johansen state = aa_dfa_match(policy->dfa, start, mntpnt);
237e2967edeSJohn Johansen state = aa_dfa_null_transition(policy->dfa, state);
2382ea3ffb7SJohn Johansen if (!state)
2392ea3ffb7SJohn Johansen return 1;
2402ea3ffb7SJohn Johansen
2412ea3ffb7SJohn Johansen if (devname)
242e2967edeSJohn Johansen state = aa_dfa_match(policy->dfa, state, devname);
243e2967edeSJohn Johansen state = aa_dfa_null_transition(policy->dfa, state);
2442ea3ffb7SJohn Johansen if (!state)
2452ea3ffb7SJohn Johansen return 2;
2462ea3ffb7SJohn Johansen
2472ea3ffb7SJohn Johansen if (type)
248e2967edeSJohn Johansen state = aa_dfa_match(policy->dfa, state, type);
249e2967edeSJohn Johansen state = aa_dfa_null_transition(policy->dfa, state);
2502ea3ffb7SJohn Johansen if (!state)
2512ea3ffb7SJohn Johansen return 3;
2522ea3ffb7SJohn Johansen
253e2967edeSJohn Johansen state = match_mnt_flags(policy->dfa, state, flags);
2542ea3ffb7SJohn Johansen if (!state)
2552ea3ffb7SJohn Johansen return 4;
256e844fe9bSJohn Johansen *perms = *aa_lookup_perms(policy, state);
2572ea3ffb7SJohn Johansen if (perms->allow & AA_MAY_MOUNT)
2582ea3ffb7SJohn Johansen return 0;
2592ea3ffb7SJohn Johansen
2602ea3ffb7SJohn Johansen /* only match data if not binary and the DFA flags data is expected */
2612ea3ffb7SJohn Johansen if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) {
262e2967edeSJohn Johansen state = aa_dfa_null_transition(policy->dfa, state);
2632ea3ffb7SJohn Johansen if (!state)
2642ea3ffb7SJohn Johansen return 4;
2652ea3ffb7SJohn Johansen
266e2967edeSJohn Johansen state = aa_dfa_match(policy->dfa, state, data);
2672ea3ffb7SJohn Johansen if (!state)
2682ea3ffb7SJohn Johansen return 5;
269e844fe9bSJohn Johansen *perms = *aa_lookup_perms(policy, state);
2702ea3ffb7SJohn Johansen if (perms->allow & AA_MAY_MOUNT)
2712ea3ffb7SJohn Johansen return 0;
2722ea3ffb7SJohn Johansen }
2732ea3ffb7SJohn Johansen
274ec240b59SJohn Johansen /* failed at perms check, don't confuse with flags match */
275ec240b59SJohn Johansen return 6;
2762ea3ffb7SJohn Johansen }
2772ea3ffb7SJohn Johansen
2782ea3ffb7SJohn Johansen
path_flags(struct aa_profile * profile,const struct path * path)2792ea3ffb7SJohn Johansen static int path_flags(struct aa_profile *profile, const struct path *path)
2802ea3ffb7SJohn Johansen {
2812ea3ffb7SJohn Johansen AA_BUG(!profile);
2822ea3ffb7SJohn Johansen AA_BUG(!path);
2832ea3ffb7SJohn Johansen
2842ea3ffb7SJohn Johansen return profile->path_flags |
2852ea3ffb7SJohn Johansen (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0);
2862ea3ffb7SJohn Johansen }
2872ea3ffb7SJohn Johansen
2882ea3ffb7SJohn Johansen /**
2892ea3ffb7SJohn Johansen * match_mnt_path_str - handle path matching for mount
290690f33e1SJohn Johansen * @subj_cred: cred of confined subject
2912ea3ffb7SJohn Johansen * @profile: the confining profile
2922ea3ffb7SJohn Johansen * @mntpath: for the mntpnt (NOT NULL)
2932ea3ffb7SJohn Johansen * @buffer: buffer to be used to lookup mntpath
294e21851b3SYang Li * @devname: string for the devname/src_name (MAY BE NULL OR ERRPTR)
2952ea3ffb7SJohn Johansen * @type: string for the dev type (MAYBE NULL)
2962ea3ffb7SJohn Johansen * @flags: mount flags to match
2972ea3ffb7SJohn Johansen * @data: fs mount data (MAYBE NULL)
2982ea3ffb7SJohn Johansen * @binary: whether @data is binary
2992ea3ffb7SJohn Johansen * @devinfo: error str if (IS_ERR(@devname))
3002ea3ffb7SJohn Johansen *
3012ea3ffb7SJohn Johansen * Returns: 0 on success else error
3022ea3ffb7SJohn Johansen */
match_mnt_path_str(const struct cred * subj_cred,struct aa_profile * profile,const struct path * mntpath,char * buffer,const char * devname,const char * type,unsigned long flags,void * data,bool binary,const char * devinfo)303690f33e1SJohn Johansen static int match_mnt_path_str(const struct cred *subj_cred,
304690f33e1SJohn Johansen struct aa_profile *profile,
3052ea3ffb7SJohn Johansen const struct path *mntpath, char *buffer,
3062ea3ffb7SJohn Johansen const char *devname, const char *type,
3072ea3ffb7SJohn Johansen unsigned long flags, void *data, bool binary,
3082ea3ffb7SJohn Johansen const char *devinfo)
3092ea3ffb7SJohn Johansen {
3102ea3ffb7SJohn Johansen struct aa_perms perms = { };
3112ea3ffb7SJohn Johansen const char *mntpnt = NULL, *info = NULL;
3121ad22fccSJohn Johansen struct aa_ruleset *rules = list_first_entry(&profile->rules,
3131ad22fccSJohn Johansen typeof(*rules), list);
3142ea3ffb7SJohn Johansen int pos, error;
3152ea3ffb7SJohn Johansen
3162ea3ffb7SJohn Johansen AA_BUG(!profile);
3172ea3ffb7SJohn Johansen AA_BUG(!mntpath);
3182ea3ffb7SJohn Johansen AA_BUG(!buffer);
3192ea3ffb7SJohn Johansen
320217af7e2SJohn Johansen if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
3215b9f57cfSJohn Johansen return 0;
3225b9f57cfSJohn Johansen
3232ea3ffb7SJohn Johansen error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
3242ea3ffb7SJohn Johansen &mntpnt, &info, profile->disconnected);
3252ea3ffb7SJohn Johansen if (error)
3262ea3ffb7SJohn Johansen goto audit;
3272ea3ffb7SJohn Johansen if (IS_ERR(devname)) {
3282ea3ffb7SJohn Johansen error = PTR_ERR(devname);
3292ea3ffb7SJohn Johansen devname = NULL;
3302ea3ffb7SJohn Johansen info = devinfo;
3312ea3ffb7SJohn Johansen goto audit;
3322ea3ffb7SJohn Johansen }
3332ea3ffb7SJohn Johansen
3342ea3ffb7SJohn Johansen error = -EACCES;
335217af7e2SJohn Johansen pos = do_match_mnt(&rules->policy,
336217af7e2SJohn Johansen rules->policy.start[AA_CLASS_MOUNT],
3372ea3ffb7SJohn Johansen mntpnt, devname, type, flags, data, binary, &perms);
3382ea3ffb7SJohn Johansen if (pos) {
3392ea3ffb7SJohn Johansen info = mnt_info_table[pos];
3402ea3ffb7SJohn Johansen goto audit;
3412ea3ffb7SJohn Johansen }
3422ea3ffb7SJohn Johansen error = 0;
3432ea3ffb7SJohn Johansen
3442ea3ffb7SJohn Johansen audit:
345690f33e1SJohn Johansen return audit_mount(subj_cred, profile, OP_MOUNT, mntpnt, devname,
346690f33e1SJohn Johansen type, NULL,
3472ea3ffb7SJohn Johansen flags, data, AA_MAY_MOUNT, &perms, info, error);
3482ea3ffb7SJohn Johansen }
3492ea3ffb7SJohn Johansen
3502ea3ffb7SJohn Johansen /**
3512ea3ffb7SJohn Johansen * match_mnt - handle path matching for mount
352690f33e1SJohn Johansen * @subj_cred: cred of the subject
3532ea3ffb7SJohn Johansen * @profile: the confining profile
354e21851b3SYang Li * @path: for the mntpnt (NOT NULL)
3552ea3ffb7SJohn Johansen * @buffer: buffer to be used to lookup mntpath
3562ea3ffb7SJohn Johansen * @devpath: path devname/src_name (MAYBE NULL)
3572ea3ffb7SJohn Johansen * @devbuffer: buffer to be used to lookup devname/src_name
3582ea3ffb7SJohn Johansen * @type: string for the dev type (MAYBE NULL)
3592ea3ffb7SJohn Johansen * @flags: mount flags to match
3602ea3ffb7SJohn Johansen * @data: fs mount data (MAYBE NULL)
3612ea3ffb7SJohn Johansen * @binary: whether @data is binary
3622ea3ffb7SJohn Johansen *
3632ea3ffb7SJohn Johansen * Returns: 0 on success else error
3642ea3ffb7SJohn Johansen */
match_mnt(const struct cred * subj_cred,struct aa_profile * profile,const struct path * path,char * buffer,const struct path * devpath,char * devbuffer,const char * type,unsigned long flags,void * data,bool binary)365690f33e1SJohn Johansen static int match_mnt(const struct cred *subj_cred,
366690f33e1SJohn Johansen struct aa_profile *profile, const struct path *path,
36764b2f34fSAl Viro char *buffer, const struct path *devpath, char *devbuffer,
3682ea3ffb7SJohn Johansen const char *type, unsigned long flags, void *data,
3692ea3ffb7SJohn Johansen bool binary)
3702ea3ffb7SJohn Johansen {
3712ea3ffb7SJohn Johansen const char *devname = NULL, *info = NULL;
3721ad22fccSJohn Johansen struct aa_ruleset *rules = list_first_entry(&profile->rules,
3731ad22fccSJohn Johansen typeof(*rules), list);
3742ea3ffb7SJohn Johansen int error = -EACCES;
3752ea3ffb7SJohn Johansen
3762ea3ffb7SJohn Johansen AA_BUG(!profile);
3772ea3ffb7SJohn Johansen AA_BUG(devpath && !devbuffer);
3782ea3ffb7SJohn Johansen
3791ad22fccSJohn Johansen if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
3805b9f57cfSJohn Johansen return 0;
3815b9f57cfSJohn Johansen
3822ea3ffb7SJohn Johansen if (devpath) {
3832ea3ffb7SJohn Johansen error = aa_path_name(devpath, path_flags(profile, devpath),
3842ea3ffb7SJohn Johansen devbuffer, &devname, &info,
3852ea3ffb7SJohn Johansen profile->disconnected);
3862ea3ffb7SJohn Johansen if (error)
3872ea3ffb7SJohn Johansen devname = ERR_PTR(error);
3882ea3ffb7SJohn Johansen }
3892ea3ffb7SJohn Johansen
390690f33e1SJohn Johansen return match_mnt_path_str(subj_cred, profile, path, buffer, devname,
391690f33e1SJohn Johansen type, flags, data, binary, info);
3922ea3ffb7SJohn Johansen }
3932ea3ffb7SJohn Johansen
aa_remount(const struct cred * subj_cred,struct aa_label * label,const struct path * path,unsigned long flags,void * data)394690f33e1SJohn Johansen int aa_remount(const struct cred *subj_cred,
395690f33e1SJohn Johansen struct aa_label *label, const struct path *path,
3962ea3ffb7SJohn Johansen unsigned long flags, void *data)
3972ea3ffb7SJohn Johansen {
3982ea3ffb7SJohn Johansen struct aa_profile *profile;
3992ea3ffb7SJohn Johansen char *buffer = NULL;
4002ea3ffb7SJohn Johansen bool binary;
4012ea3ffb7SJohn Johansen int error;
4022ea3ffb7SJohn Johansen
4032ea3ffb7SJohn Johansen AA_BUG(!label);
4042ea3ffb7SJohn Johansen AA_BUG(!path);
4052ea3ffb7SJohn Johansen
4062ea3ffb7SJohn Johansen binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
4072ea3ffb7SJohn Johansen
408341c1fdaSJohn Johansen buffer = aa_get_buffer(false);
409df323337SSebastian Andrzej Siewior if (!buffer)
410df323337SSebastian Andrzej Siewior return -ENOMEM;
4112ea3ffb7SJohn Johansen error = fn_for_each_confined(label, profile,
412690f33e1SJohn Johansen match_mnt(subj_cred, profile, path, buffer, NULL,
413690f33e1SJohn Johansen NULL, NULL,
4142ea3ffb7SJohn Johansen flags, data, binary));
415df323337SSebastian Andrzej Siewior aa_put_buffer(buffer);
4162ea3ffb7SJohn Johansen
4172ea3ffb7SJohn Johansen return error;
4182ea3ffb7SJohn Johansen }
4192ea3ffb7SJohn Johansen
aa_bind_mount(const struct cred * subj_cred,struct aa_label * label,const struct path * path,const char * dev_name,unsigned long flags)420690f33e1SJohn Johansen int aa_bind_mount(const struct cred *subj_cred,
421690f33e1SJohn Johansen struct aa_label *label, const struct path *path,
4222ea3ffb7SJohn Johansen const char *dev_name, unsigned long flags)
4232ea3ffb7SJohn Johansen {
4242ea3ffb7SJohn Johansen struct aa_profile *profile;
4252ea3ffb7SJohn Johansen char *buffer = NULL, *old_buffer = NULL;
4262ea3ffb7SJohn Johansen struct path old_path;
4272ea3ffb7SJohn Johansen int error;
4282ea3ffb7SJohn Johansen
4292ea3ffb7SJohn Johansen AA_BUG(!label);
4302ea3ffb7SJohn Johansen AA_BUG(!path);
4312ea3ffb7SJohn Johansen
4322ea3ffb7SJohn Johansen if (!dev_name || !*dev_name)
4332ea3ffb7SJohn Johansen return -EINVAL;
4342ea3ffb7SJohn Johansen
4352ea3ffb7SJohn Johansen flags &= MS_REC | MS_BIND;
4362ea3ffb7SJohn Johansen
4372ea3ffb7SJohn Johansen error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
4382ea3ffb7SJohn Johansen if (error)
4392ea3ffb7SJohn Johansen return error;
4402ea3ffb7SJohn Johansen
441341c1fdaSJohn Johansen buffer = aa_get_buffer(false);
442341c1fdaSJohn Johansen old_buffer = aa_get_buffer(false);
443df323337SSebastian Andrzej Siewior error = -ENOMEM;
4449c95a278SPatrick Steinhardt if (!buffer || !old_buffer)
445df323337SSebastian Andrzej Siewior goto out;
446df323337SSebastian Andrzej Siewior
4472ea3ffb7SJohn Johansen error = fn_for_each_confined(label, profile,
448690f33e1SJohn Johansen match_mnt(subj_cred, profile, path, buffer, &old_path,
449690f33e1SJohn Johansen old_buffer, NULL, flags, NULL, false));
450df323337SSebastian Andrzej Siewior out:
451df323337SSebastian Andrzej Siewior aa_put_buffer(buffer);
452df323337SSebastian Andrzej Siewior aa_put_buffer(old_buffer);
4532ea3ffb7SJohn Johansen path_put(&old_path);
4542ea3ffb7SJohn Johansen
4552ea3ffb7SJohn Johansen return error;
4562ea3ffb7SJohn Johansen }
4572ea3ffb7SJohn Johansen
aa_mount_change_type(const struct cred * subj_cred,struct aa_label * label,const struct path * path,unsigned long flags)458690f33e1SJohn Johansen int aa_mount_change_type(const struct cred *subj_cred,
459690f33e1SJohn Johansen struct aa_label *label, const struct path *path,
4602ea3ffb7SJohn Johansen unsigned long flags)
4612ea3ffb7SJohn Johansen {
4622ea3ffb7SJohn Johansen struct aa_profile *profile;
4632ea3ffb7SJohn Johansen char *buffer = NULL;
4642ea3ffb7SJohn Johansen int error;
4652ea3ffb7SJohn Johansen
4662ea3ffb7SJohn Johansen AA_BUG(!label);
4672ea3ffb7SJohn Johansen AA_BUG(!path);
4682ea3ffb7SJohn Johansen
4692ea3ffb7SJohn Johansen /* These are the flags allowed by do_change_type() */
4702ea3ffb7SJohn Johansen flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
4712ea3ffb7SJohn Johansen MS_UNBINDABLE);
4722ea3ffb7SJohn Johansen
473341c1fdaSJohn Johansen buffer = aa_get_buffer(false);
474df323337SSebastian Andrzej Siewior if (!buffer)
475df323337SSebastian Andrzej Siewior return -ENOMEM;
4762ea3ffb7SJohn Johansen error = fn_for_each_confined(label, profile,
477690f33e1SJohn Johansen match_mnt(subj_cred, profile, path, buffer, NULL,
478690f33e1SJohn Johansen NULL, NULL,
4792ea3ffb7SJohn Johansen flags, NULL, false));
480df323337SSebastian Andrzej Siewior aa_put_buffer(buffer);
4812ea3ffb7SJohn Johansen
4822ea3ffb7SJohn Johansen return error;
4832ea3ffb7SJohn Johansen }
4842ea3ffb7SJohn Johansen
aa_move_mount(const struct cred * subj_cred,struct aa_label * label,const struct path * from_path,const struct path * to_path)485690f33e1SJohn Johansen int aa_move_mount(const struct cred *subj_cred,
48696af4515SJohn Johansen struct aa_label *label, const struct path *from_path,
48796af4515SJohn Johansen const struct path *to_path)
4882ea3ffb7SJohn Johansen {
4892ea3ffb7SJohn Johansen struct aa_profile *profile;
49096af4515SJohn Johansen char *to_buffer = NULL, *from_buffer = NULL;
4912ea3ffb7SJohn Johansen int error;
4922ea3ffb7SJohn Johansen
4932ea3ffb7SJohn Johansen AA_BUG(!label);
49496af4515SJohn Johansen AA_BUG(!from_path);
49596af4515SJohn Johansen AA_BUG(!to_path);
49696af4515SJohn Johansen
49796af4515SJohn Johansen to_buffer = aa_get_buffer(false);
49896af4515SJohn Johansen from_buffer = aa_get_buffer(false);
49996af4515SJohn Johansen error = -ENOMEM;
50096af4515SJohn Johansen if (!to_buffer || !from_buffer)
50196af4515SJohn Johansen goto out;
502*6d2e4e56SJohn Johansen
503*6d2e4e56SJohn Johansen if (!our_mnt(from_path->mnt))
504*6d2e4e56SJohn Johansen /* moving a mount detached from the namespace */
505*6d2e4e56SJohn Johansen from_path = NULL;
50696af4515SJohn Johansen error = fn_for_each_confined(label, profile,
50796af4515SJohn Johansen match_mnt(subj_cred, profile, to_path, to_buffer,
50896af4515SJohn Johansen from_path, from_buffer,
50996af4515SJohn Johansen NULL, MS_MOVE, NULL, false));
51096af4515SJohn Johansen out:
51196af4515SJohn Johansen aa_put_buffer(to_buffer);
51296af4515SJohn Johansen aa_put_buffer(from_buffer);
51396af4515SJohn Johansen
51496af4515SJohn Johansen return error;
51596af4515SJohn Johansen }
51696af4515SJohn Johansen
aa_move_mount_old(const struct cred * subj_cred,struct aa_label * label,const struct path * path,const char * orig_name)51796af4515SJohn Johansen int aa_move_mount_old(const struct cred *subj_cred, struct aa_label *label,
51896af4515SJohn Johansen const struct path *path, const char *orig_name)
51996af4515SJohn Johansen {
52096af4515SJohn Johansen struct path old_path;
52196af4515SJohn Johansen int error;
5222ea3ffb7SJohn Johansen
5232ea3ffb7SJohn Johansen if (!orig_name || !*orig_name)
5242ea3ffb7SJohn Johansen return -EINVAL;
5252ea3ffb7SJohn Johansen error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
5262ea3ffb7SJohn Johansen if (error)
5272ea3ffb7SJohn Johansen return error;
5282ea3ffb7SJohn Johansen
52996af4515SJohn Johansen error = aa_move_mount(subj_cred, label, &old_path, path);
5302ea3ffb7SJohn Johansen path_put(&old_path);
5312ea3ffb7SJohn Johansen
5322ea3ffb7SJohn Johansen return error;
5332ea3ffb7SJohn Johansen }
5342ea3ffb7SJohn Johansen
aa_new_mount(const struct cred * subj_cred,struct aa_label * label,const char * dev_name,const struct path * path,const char * type,unsigned long flags,void * data)535690f33e1SJohn Johansen int aa_new_mount(const struct cred *subj_cred, struct aa_label *label,
536690f33e1SJohn Johansen const char *dev_name, const struct path *path,
537690f33e1SJohn Johansen const char *type, unsigned long flags, void *data)
5382ea3ffb7SJohn Johansen {
5392ea3ffb7SJohn Johansen struct aa_profile *profile;
5402ea3ffb7SJohn Johansen char *buffer = NULL, *dev_buffer = NULL;
5412ea3ffb7SJohn Johansen bool binary = true;
5422ea3ffb7SJohn Johansen int error;
5432ea3ffb7SJohn Johansen int requires_dev = 0;
5442ea3ffb7SJohn Johansen struct path tmp_path, *dev_path = NULL;
5452ea3ffb7SJohn Johansen
5462ea3ffb7SJohn Johansen AA_BUG(!label);
5472ea3ffb7SJohn Johansen AA_BUG(!path);
5482ea3ffb7SJohn Johansen
5492ea3ffb7SJohn Johansen if (type) {
5502ea3ffb7SJohn Johansen struct file_system_type *fstype;
5512ea3ffb7SJohn Johansen
5522ea3ffb7SJohn Johansen fstype = get_fs_type(type);
5532ea3ffb7SJohn Johansen if (!fstype)
5542ea3ffb7SJohn Johansen return -ENODEV;
5552ea3ffb7SJohn Johansen binary = fstype->fs_flags & FS_BINARY_MOUNTDATA;
5562ea3ffb7SJohn Johansen requires_dev = fstype->fs_flags & FS_REQUIRES_DEV;
5572ea3ffb7SJohn Johansen put_filesystem(fstype);
5582ea3ffb7SJohn Johansen
5592ea3ffb7SJohn Johansen if (requires_dev) {
5602ea3ffb7SJohn Johansen if (!dev_name || !*dev_name)
5612ea3ffb7SJohn Johansen return -ENOENT;
5622ea3ffb7SJohn Johansen
5632ea3ffb7SJohn Johansen error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path);
5642ea3ffb7SJohn Johansen if (error)
5652ea3ffb7SJohn Johansen return error;
5662ea3ffb7SJohn Johansen dev_path = &tmp_path;
5672ea3ffb7SJohn Johansen }
5682ea3ffb7SJohn Johansen }
5692ea3ffb7SJohn Johansen
570341c1fdaSJohn Johansen buffer = aa_get_buffer(false);
571df323337SSebastian Andrzej Siewior if (!buffer) {
572df323337SSebastian Andrzej Siewior error = -ENOMEM;
573df323337SSebastian Andrzej Siewior goto out;
574df323337SSebastian Andrzej Siewior }
5752ea3ffb7SJohn Johansen if (dev_path) {
576341c1fdaSJohn Johansen dev_buffer = aa_get_buffer(false);
577df323337SSebastian Andrzej Siewior if (!dev_buffer) {
578df323337SSebastian Andrzej Siewior error = -ENOMEM;
579df323337SSebastian Andrzej Siewior goto out;
580df323337SSebastian Andrzej Siewior }
5812ea3ffb7SJohn Johansen error = fn_for_each_confined(label, profile,
582690f33e1SJohn Johansen match_mnt(subj_cred, profile, path, buffer,
583690f33e1SJohn Johansen dev_path, dev_buffer,
5842ea3ffb7SJohn Johansen type, flags, data, binary));
5852ea3ffb7SJohn Johansen } else {
5862ea3ffb7SJohn Johansen error = fn_for_each_confined(label, profile,
587690f33e1SJohn Johansen match_mnt_path_str(subj_cred, profile, path,
588690f33e1SJohn Johansen buffer, dev_name,
5892ea3ffb7SJohn Johansen type, flags, data, binary, NULL));
5902ea3ffb7SJohn Johansen }
591df323337SSebastian Andrzej Siewior
592df323337SSebastian Andrzej Siewior out:
593df323337SSebastian Andrzej Siewior aa_put_buffer(buffer);
594df323337SSebastian Andrzej Siewior aa_put_buffer(dev_buffer);
5952ea3ffb7SJohn Johansen if (dev_path)
5962ea3ffb7SJohn Johansen path_put(dev_path);
5972ea3ffb7SJohn Johansen
5982ea3ffb7SJohn Johansen return error;
5992ea3ffb7SJohn Johansen }
6002ea3ffb7SJohn Johansen
profile_umount(const struct cred * subj_cred,struct aa_profile * profile,const struct path * path,char * buffer)601690f33e1SJohn Johansen static int profile_umount(const struct cred *subj_cred,
602690f33e1SJohn Johansen struct aa_profile *profile, const struct path *path,
6032ea3ffb7SJohn Johansen char *buffer)
6042ea3ffb7SJohn Johansen {
6051ad22fccSJohn Johansen struct aa_ruleset *rules = list_first_entry(&profile->rules,
6061ad22fccSJohn Johansen typeof(*rules), list);
6072ea3ffb7SJohn Johansen struct aa_perms perms = { };
6082ea3ffb7SJohn Johansen const char *name = NULL, *info = NULL;
60933fc95d8SJohn Johansen aa_state_t state;
6102ea3ffb7SJohn Johansen int error;
6112ea3ffb7SJohn Johansen
6122ea3ffb7SJohn Johansen AA_BUG(!profile);
6132ea3ffb7SJohn Johansen AA_BUG(!path);
6142ea3ffb7SJohn Johansen
615217af7e2SJohn Johansen if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
6165b9f57cfSJohn Johansen return 0;
6175b9f57cfSJohn Johansen
6182ea3ffb7SJohn Johansen error = aa_path_name(path, path_flags(profile, path), buffer, &name,
6192ea3ffb7SJohn Johansen &info, profile->disconnected);
6202ea3ffb7SJohn Johansen if (error)
6212ea3ffb7SJohn Johansen goto audit;
6222ea3ffb7SJohn Johansen
623217af7e2SJohn Johansen state = aa_dfa_match(rules->policy.dfa,
624217af7e2SJohn Johansen rules->policy.start[AA_CLASS_MOUNT],
6252ea3ffb7SJohn Johansen name);
626217af7e2SJohn Johansen perms = *aa_lookup_perms(&rules->policy, state);
6272ea3ffb7SJohn Johansen if (AA_MAY_UMOUNT & ~perms.allow)
6282ea3ffb7SJohn Johansen error = -EACCES;
6292ea3ffb7SJohn Johansen
6302ea3ffb7SJohn Johansen audit:
631690f33e1SJohn Johansen return audit_mount(subj_cred, profile, OP_UMOUNT, name, NULL, NULL,
632690f33e1SJohn Johansen NULL, 0, NULL,
6332ea3ffb7SJohn Johansen AA_MAY_UMOUNT, &perms, info, error);
6342ea3ffb7SJohn Johansen }
6352ea3ffb7SJohn Johansen
aa_umount(const struct cred * subj_cred,struct aa_label * label,struct vfsmount * mnt,int flags)636690f33e1SJohn Johansen int aa_umount(const struct cred *subj_cred, struct aa_label *label,
637690f33e1SJohn Johansen struct vfsmount *mnt, int flags)
6382ea3ffb7SJohn Johansen {
6392ea3ffb7SJohn Johansen struct aa_profile *profile;
6402ea3ffb7SJohn Johansen char *buffer = NULL;
6412ea3ffb7SJohn Johansen int error;
6422ea3ffb7SJohn Johansen struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
6432ea3ffb7SJohn Johansen
6442ea3ffb7SJohn Johansen AA_BUG(!label);
6452ea3ffb7SJohn Johansen AA_BUG(!mnt);
6462ea3ffb7SJohn Johansen
647341c1fdaSJohn Johansen buffer = aa_get_buffer(false);
648df323337SSebastian Andrzej Siewior if (!buffer)
649df323337SSebastian Andrzej Siewior return -ENOMEM;
650df323337SSebastian Andrzej Siewior
6512ea3ffb7SJohn Johansen error = fn_for_each_confined(label, profile,
652690f33e1SJohn Johansen profile_umount(subj_cred, profile, &path, buffer));
653df323337SSebastian Andrzej Siewior aa_put_buffer(buffer);
6542ea3ffb7SJohn Johansen
6552ea3ffb7SJohn Johansen return error;
6562ea3ffb7SJohn Johansen }
6572ea3ffb7SJohn Johansen
6582ea3ffb7SJohn Johansen /* helper fn for transition on pivotroot
6592ea3ffb7SJohn Johansen *
6602ea3ffb7SJohn Johansen * Returns: label for transition or ERR_PTR. Does not return NULL
6612ea3ffb7SJohn Johansen */
build_pivotroot(const struct cred * subj_cred,struct aa_profile * profile,const struct path * new_path,char * new_buffer,const struct path * old_path,char * old_buffer)662690f33e1SJohn Johansen static struct aa_label *build_pivotroot(const struct cred *subj_cred,
663690f33e1SJohn Johansen struct aa_profile *profile,
6642ea3ffb7SJohn Johansen const struct path *new_path,
6652ea3ffb7SJohn Johansen char *new_buffer,
6662ea3ffb7SJohn Johansen const struct path *old_path,
6672ea3ffb7SJohn Johansen char *old_buffer)
6682ea3ffb7SJohn Johansen {
6691ad22fccSJohn Johansen struct aa_ruleset *rules = list_first_entry(&profile->rules,
6701ad22fccSJohn Johansen typeof(*rules), list);
6712ea3ffb7SJohn Johansen const char *old_name, *new_name = NULL, *info = NULL;
6722ea3ffb7SJohn Johansen const char *trans_name = NULL;
6732ea3ffb7SJohn Johansen struct aa_perms perms = { };
67433fc95d8SJohn Johansen aa_state_t state;
6752ea3ffb7SJohn Johansen int error;
6762ea3ffb7SJohn Johansen
6772ea3ffb7SJohn Johansen AA_BUG(!profile);
6782ea3ffb7SJohn Johansen AA_BUG(!new_path);
6792ea3ffb7SJohn Johansen AA_BUG(!old_path);
6802ea3ffb7SJohn Johansen
6815b9f57cfSJohn Johansen if (profile_unconfined(profile) ||
682217af7e2SJohn Johansen !RULE_MEDIATES(rules, AA_CLASS_MOUNT))
6832ea3ffb7SJohn Johansen return aa_get_newest_label(&profile->label);
6842ea3ffb7SJohn Johansen
6852ea3ffb7SJohn Johansen error = aa_path_name(old_path, path_flags(profile, old_path),
6862ea3ffb7SJohn Johansen old_buffer, &old_name, &info,
6872ea3ffb7SJohn Johansen profile->disconnected);
6882ea3ffb7SJohn Johansen if (error)
6892ea3ffb7SJohn Johansen goto audit;
6902ea3ffb7SJohn Johansen error = aa_path_name(new_path, path_flags(profile, new_path),
6912ea3ffb7SJohn Johansen new_buffer, &new_name, &info,
6922ea3ffb7SJohn Johansen profile->disconnected);
6932ea3ffb7SJohn Johansen if (error)
6942ea3ffb7SJohn Johansen goto audit;
6952ea3ffb7SJohn Johansen
6962ea3ffb7SJohn Johansen error = -EACCES;
697217af7e2SJohn Johansen state = aa_dfa_match(rules->policy.dfa,
698217af7e2SJohn Johansen rules->policy.start[AA_CLASS_MOUNT],
6992ea3ffb7SJohn Johansen new_name);
700217af7e2SJohn Johansen state = aa_dfa_null_transition(rules->policy.dfa, state);
701217af7e2SJohn Johansen state = aa_dfa_match(rules->policy.dfa, state, old_name);
702217af7e2SJohn Johansen perms = *aa_lookup_perms(&rules->policy, state);
7032ea3ffb7SJohn Johansen
7042ea3ffb7SJohn Johansen if (AA_MAY_PIVOTROOT & perms.allow)
7052ea3ffb7SJohn Johansen error = 0;
7062ea3ffb7SJohn Johansen
7072ea3ffb7SJohn Johansen audit:
708690f33e1SJohn Johansen error = audit_mount(subj_cred, profile, OP_PIVOTROOT, new_name,
709690f33e1SJohn Johansen old_name,
7102ea3ffb7SJohn Johansen NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT,
7112ea3ffb7SJohn Johansen &perms, info, error);
7122ea3ffb7SJohn Johansen if (error)
7132ea3ffb7SJohn Johansen return ERR_PTR(error);
7142ea3ffb7SJohn Johansen
7152ea3ffb7SJohn Johansen return aa_get_newest_label(&profile->label);
7162ea3ffb7SJohn Johansen }
7172ea3ffb7SJohn Johansen
aa_pivotroot(const struct cred * subj_cred,struct aa_label * label,const struct path * old_path,const struct path * new_path)718690f33e1SJohn Johansen int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
719690f33e1SJohn Johansen const struct path *old_path,
7202ea3ffb7SJohn Johansen const struct path *new_path)
7212ea3ffb7SJohn Johansen {
7222ea3ffb7SJohn Johansen struct aa_profile *profile;
7232ea3ffb7SJohn Johansen struct aa_label *target = NULL;
7242ea3ffb7SJohn Johansen char *old_buffer = NULL, *new_buffer = NULL, *info = NULL;
7252ea3ffb7SJohn Johansen int error;
7262ea3ffb7SJohn Johansen
7272ea3ffb7SJohn Johansen AA_BUG(!label);
7282ea3ffb7SJohn Johansen AA_BUG(!old_path);
7292ea3ffb7SJohn Johansen AA_BUG(!new_path);
7302ea3ffb7SJohn Johansen
731341c1fdaSJohn Johansen old_buffer = aa_get_buffer(false);
732341c1fdaSJohn Johansen new_buffer = aa_get_buffer(false);
733df323337SSebastian Andrzej Siewior error = -ENOMEM;
734df323337SSebastian Andrzej Siewior if (!old_buffer || !new_buffer)
735df323337SSebastian Andrzej Siewior goto out;
7368ac2ca32SSebastian Andrzej Siewior target = fn_label_build(label, profile, GFP_KERNEL,
737690f33e1SJohn Johansen build_pivotroot(subj_cred, profile, new_path,
738690f33e1SJohn Johansen new_buffer,
7392ea3ffb7SJohn Johansen old_path, old_buffer));
7402ea3ffb7SJohn Johansen if (!target) {
7412ea3ffb7SJohn Johansen info = "label build failed";
7422ea3ffb7SJohn Johansen error = -ENOMEM;
7432ea3ffb7SJohn Johansen goto fail;
7442ea3ffb7SJohn Johansen } else if (!IS_ERR(target)) {
7452ea3ffb7SJohn Johansen error = aa_replace_current_label(target);
7462ea3ffb7SJohn Johansen if (error) {
7472ea3ffb7SJohn Johansen /* TODO: audit target */
7482ea3ffb7SJohn Johansen aa_put_label(target);
7492ea3ffb7SJohn Johansen goto out;
7502ea3ffb7SJohn Johansen }
75111c3627eSXin Xiong aa_put_label(target);
7522ea3ffb7SJohn Johansen } else
7532ea3ffb7SJohn Johansen /* already audited error */
7542ea3ffb7SJohn Johansen error = PTR_ERR(target);
7552ea3ffb7SJohn Johansen out:
756df323337SSebastian Andrzej Siewior aa_put_buffer(old_buffer);
757df323337SSebastian Andrzej Siewior aa_put_buffer(new_buffer);
7582ea3ffb7SJohn Johansen
7592ea3ffb7SJohn Johansen return error;
7602ea3ffb7SJohn Johansen
7612ea3ffb7SJohn Johansen fail:
7622ea3ffb7SJohn Johansen /* TODO: add back in auditing of new_name and old_name */
7632ea3ffb7SJohn Johansen error = fn_for_each(label, profile,
764690f33e1SJohn Johansen audit_mount(subj_cred, profile, OP_PIVOTROOT,
765690f33e1SJohn Johansen NULL /*new_name */,
7662ea3ffb7SJohn Johansen NULL /* old_name */,
7672ea3ffb7SJohn Johansen NULL, NULL,
7682ea3ffb7SJohn Johansen 0, NULL, AA_MAY_PIVOTROOT, &nullperms, info,
7692ea3ffb7SJohn Johansen error));
7702ea3ffb7SJohn Johansen goto out;
7712ea3ffb7SJohn Johansen }
772