xref: /openbmc/linux/security/apparmor/mount.c (revision 59f5a149f5072f4da79656cde9972ba2b616634f)
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