162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AppArmor security module
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This file contains AppArmor mediation of files
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 1998-2008 Novell/SUSE
862306a36Sopenharmony_ci * Copyright 2009-2017 Canonical Ltd.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/mount.h>
1362306a36Sopenharmony_ci#include <linux/namei.h>
1462306a36Sopenharmony_ci#include <uapi/linux/mount.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "include/apparmor.h"
1762306a36Sopenharmony_ci#include "include/audit.h"
1862306a36Sopenharmony_ci#include "include/cred.h"
1962306a36Sopenharmony_ci#include "include/domain.h"
2062306a36Sopenharmony_ci#include "include/file.h"
2162306a36Sopenharmony_ci#include "include/match.h"
2262306a36Sopenharmony_ci#include "include/mount.h"
2362306a36Sopenharmony_ci#include "include/path.h"
2462306a36Sopenharmony_ci#include "include/policy.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	if (flags & MS_RDONLY)
3062306a36Sopenharmony_ci		audit_log_format(ab, "ro");
3162306a36Sopenharmony_ci	else
3262306a36Sopenharmony_ci		audit_log_format(ab, "rw");
3362306a36Sopenharmony_ci	if (flags & MS_NOSUID)
3462306a36Sopenharmony_ci		audit_log_format(ab, ", nosuid");
3562306a36Sopenharmony_ci	if (flags & MS_NODEV)
3662306a36Sopenharmony_ci		audit_log_format(ab, ", nodev");
3762306a36Sopenharmony_ci	if (flags & MS_NOEXEC)
3862306a36Sopenharmony_ci		audit_log_format(ab, ", noexec");
3962306a36Sopenharmony_ci	if (flags & MS_SYNCHRONOUS)
4062306a36Sopenharmony_ci		audit_log_format(ab, ", sync");
4162306a36Sopenharmony_ci	if (flags & MS_REMOUNT)
4262306a36Sopenharmony_ci		audit_log_format(ab, ", remount");
4362306a36Sopenharmony_ci	if (flags & MS_MANDLOCK)
4462306a36Sopenharmony_ci		audit_log_format(ab, ", mand");
4562306a36Sopenharmony_ci	if (flags & MS_DIRSYNC)
4662306a36Sopenharmony_ci		audit_log_format(ab, ", dirsync");
4762306a36Sopenharmony_ci	if (flags & MS_NOATIME)
4862306a36Sopenharmony_ci		audit_log_format(ab, ", noatime");
4962306a36Sopenharmony_ci	if (flags & MS_NODIRATIME)
5062306a36Sopenharmony_ci		audit_log_format(ab, ", nodiratime");
5162306a36Sopenharmony_ci	if (flags & MS_BIND)
5262306a36Sopenharmony_ci		audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind");
5362306a36Sopenharmony_ci	if (flags & MS_MOVE)
5462306a36Sopenharmony_ci		audit_log_format(ab, ", move");
5562306a36Sopenharmony_ci	if (flags & MS_SILENT)
5662306a36Sopenharmony_ci		audit_log_format(ab, ", silent");
5762306a36Sopenharmony_ci	if (flags & MS_POSIXACL)
5862306a36Sopenharmony_ci		audit_log_format(ab, ", acl");
5962306a36Sopenharmony_ci	if (flags & MS_UNBINDABLE)
6062306a36Sopenharmony_ci		audit_log_format(ab, flags & MS_REC ? ", runbindable" :
6162306a36Sopenharmony_ci				 ", unbindable");
6262306a36Sopenharmony_ci	if (flags & MS_PRIVATE)
6362306a36Sopenharmony_ci		audit_log_format(ab, flags & MS_REC ? ", rprivate" :
6462306a36Sopenharmony_ci				 ", private");
6562306a36Sopenharmony_ci	if (flags & MS_SLAVE)
6662306a36Sopenharmony_ci		audit_log_format(ab, flags & MS_REC ? ", rslave" :
6762306a36Sopenharmony_ci				 ", slave");
6862306a36Sopenharmony_ci	if (flags & MS_SHARED)
6962306a36Sopenharmony_ci		audit_log_format(ab, flags & MS_REC ? ", rshared" :
7062306a36Sopenharmony_ci				 ", shared");
7162306a36Sopenharmony_ci	if (flags & MS_RELATIME)
7262306a36Sopenharmony_ci		audit_log_format(ab, ", relatime");
7362306a36Sopenharmony_ci	if (flags & MS_I_VERSION)
7462306a36Sopenharmony_ci		audit_log_format(ab, ", iversion");
7562306a36Sopenharmony_ci	if (flags & MS_STRICTATIME)
7662306a36Sopenharmony_ci		audit_log_format(ab, ", strictatime");
7762306a36Sopenharmony_ci	if (flags & MS_NOUSER)
7862306a36Sopenharmony_ci		audit_log_format(ab, ", nouser");
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/**
8262306a36Sopenharmony_ci * audit_cb - call back for mount specific audit fields
8362306a36Sopenharmony_ci * @ab: audit_buffer  (NOT NULL)
8462306a36Sopenharmony_ci * @va: audit struct to audit values of  (NOT NULL)
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_cistatic void audit_cb(struct audit_buffer *ab, void *va)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct common_audit_data *sa = va;
8962306a36Sopenharmony_ci	struct apparmor_audit_data *ad = aad(sa);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (ad->mnt.type) {
9262306a36Sopenharmony_ci		audit_log_format(ab, " fstype=");
9362306a36Sopenharmony_ci		audit_log_untrustedstring(ab, ad->mnt.type);
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci	if (ad->mnt.src_name) {
9662306a36Sopenharmony_ci		audit_log_format(ab, " srcname=");
9762306a36Sopenharmony_ci		audit_log_untrustedstring(ab, ad->mnt.src_name);
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci	if (ad->mnt.trans) {
10062306a36Sopenharmony_ci		audit_log_format(ab, " trans=");
10162306a36Sopenharmony_ci		audit_log_untrustedstring(ab, ad->mnt.trans);
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci	if (ad->mnt.flags) {
10462306a36Sopenharmony_ci		audit_log_format(ab, " flags=\"");
10562306a36Sopenharmony_ci		audit_mnt_flags(ab, ad->mnt.flags);
10662306a36Sopenharmony_ci		audit_log_format(ab, "\"");
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci	if (ad->mnt.data) {
10962306a36Sopenharmony_ci		audit_log_format(ab, " options=");
11062306a36Sopenharmony_ci		audit_log_untrustedstring(ab, ad->mnt.data);
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/**
11562306a36Sopenharmony_ci * audit_mount - handle the auditing of mount operations
11662306a36Sopenharmony_ci * @subj_cred: cred of the subject
11762306a36Sopenharmony_ci * @profile: the profile being enforced  (NOT NULL)
11862306a36Sopenharmony_ci * @op: operation being mediated (NOT NULL)
11962306a36Sopenharmony_ci * @name: name of object being mediated (MAYBE NULL)
12062306a36Sopenharmony_ci * @src_name: src_name of object being mediated (MAYBE_NULL)
12162306a36Sopenharmony_ci * @type: type of filesystem (MAYBE_NULL)
12262306a36Sopenharmony_ci * @trans: name of trans (MAYBE NULL)
12362306a36Sopenharmony_ci * @flags: filesystem independent mount flags
12462306a36Sopenharmony_ci * @data: filesystem mount flags
12562306a36Sopenharmony_ci * @request: permissions requested
12662306a36Sopenharmony_ci * @perms: the permissions computed for the request (NOT NULL)
12762306a36Sopenharmony_ci * @info: extra information message (MAYBE NULL)
12862306a36Sopenharmony_ci * @error: 0 if operation allowed else failure error code
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci * Returns: %0 or error on failure
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_cistatic int audit_mount(const struct cred *subj_cred,
13362306a36Sopenharmony_ci		       struct aa_profile *profile, const char *op,
13462306a36Sopenharmony_ci		       const char *name, const char *src_name,
13562306a36Sopenharmony_ci		       const char *type, const char *trans,
13662306a36Sopenharmony_ci		       unsigned long flags, const void *data, u32 request,
13762306a36Sopenharmony_ci		       struct aa_perms *perms, const char *info, int error)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	int audit_type = AUDIT_APPARMOR_AUTO;
14062306a36Sopenharmony_ci	DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_MOUNT, op);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (likely(!error)) {
14362306a36Sopenharmony_ci		u32 mask = perms->audit;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
14662306a36Sopenharmony_ci			mask = 0xffff;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		/* mask off perms that are not being force audited */
14962306a36Sopenharmony_ci		request &= mask;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		if (likely(!request))
15262306a36Sopenharmony_ci			return 0;
15362306a36Sopenharmony_ci		audit_type = AUDIT_APPARMOR_AUDIT;
15462306a36Sopenharmony_ci	} else {
15562306a36Sopenharmony_ci		/* only report permissions that were denied */
15662306a36Sopenharmony_ci		request = request & ~perms->allow;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		if (request & perms->kill)
15962306a36Sopenharmony_ci			audit_type = AUDIT_APPARMOR_KILL;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		/* quiet known rejects, assumes quiet and kill do not overlap */
16262306a36Sopenharmony_ci		if ((request & perms->quiet) &&
16362306a36Sopenharmony_ci		    AUDIT_MODE(profile) != AUDIT_NOQUIET &&
16462306a36Sopenharmony_ci		    AUDIT_MODE(profile) != AUDIT_ALL)
16562306a36Sopenharmony_ci			request &= ~perms->quiet;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		if (!request)
16862306a36Sopenharmony_ci			return error;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	ad.subj_cred = subj_cred;
17262306a36Sopenharmony_ci	ad.name = name;
17362306a36Sopenharmony_ci	ad.mnt.src_name = src_name;
17462306a36Sopenharmony_ci	ad.mnt.type = type;
17562306a36Sopenharmony_ci	ad.mnt.trans = trans;
17662306a36Sopenharmony_ci	ad.mnt.flags = flags;
17762306a36Sopenharmony_ci	if (data && (perms->audit & AA_AUDIT_DATA))
17862306a36Sopenharmony_ci		ad.mnt.data = data;
17962306a36Sopenharmony_ci	ad.info = info;
18062306a36Sopenharmony_ci	ad.error = error;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return aa_audit(audit_type, profile, &ad, audit_cb);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/**
18662306a36Sopenharmony_ci * match_mnt_flags - Do an ordered match on mount flags
18762306a36Sopenharmony_ci * @dfa: dfa to match against
18862306a36Sopenharmony_ci * @state: state to start in
18962306a36Sopenharmony_ci * @flags: mount flags to match against
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * Mount flags are encoded as an ordered match. This is done instead of
19262306a36Sopenharmony_ci * checking against a simple bitmask, to allow for logical operations
19362306a36Sopenharmony_ci * on the flags.
19462306a36Sopenharmony_ci *
19562306a36Sopenharmony_ci * Returns: next state after flags match
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_cistatic aa_state_t match_mnt_flags(struct aa_dfa *dfa, aa_state_t state,
19862306a36Sopenharmony_ci				    unsigned long flags)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	unsigned int i;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	for (i = 0; i <= 31 ; ++i) {
20362306a36Sopenharmony_ci		if ((1 << i) & flags)
20462306a36Sopenharmony_ci			state = aa_dfa_next(dfa, state, i + 1);
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return state;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic const char * const mnt_info_table[] = {
21162306a36Sopenharmony_ci	"match succeeded",
21262306a36Sopenharmony_ci	"failed mntpnt match",
21362306a36Sopenharmony_ci	"failed srcname match",
21462306a36Sopenharmony_ci	"failed type match",
21562306a36Sopenharmony_ci	"failed flags match",
21662306a36Sopenharmony_ci	"failed data match",
21762306a36Sopenharmony_ci	"failed perms check"
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/*
22162306a36Sopenharmony_ci * Returns 0 on success else element that match failed in, this is the
22262306a36Sopenharmony_ci * index into the mnt_info_table above
22362306a36Sopenharmony_ci */
22462306a36Sopenharmony_cistatic int do_match_mnt(struct aa_policydb *policy, aa_state_t start,
22562306a36Sopenharmony_ci			const char *mntpnt, const char *devname,
22662306a36Sopenharmony_ci			const char *type, unsigned long flags,
22762306a36Sopenharmony_ci			void *data, bool binary, struct aa_perms *perms)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	aa_state_t state;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	AA_BUG(!policy);
23262306a36Sopenharmony_ci	AA_BUG(!policy->dfa);
23362306a36Sopenharmony_ci	AA_BUG(!policy->perms);
23462306a36Sopenharmony_ci	AA_BUG(!perms);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	state = aa_dfa_match(policy->dfa, start, mntpnt);
23762306a36Sopenharmony_ci	state = aa_dfa_null_transition(policy->dfa, state);
23862306a36Sopenharmony_ci	if (!state)
23962306a36Sopenharmony_ci		return 1;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (devname)
24262306a36Sopenharmony_ci		state = aa_dfa_match(policy->dfa, state, devname);
24362306a36Sopenharmony_ci	state = aa_dfa_null_transition(policy->dfa, state);
24462306a36Sopenharmony_ci	if (!state)
24562306a36Sopenharmony_ci		return 2;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (type)
24862306a36Sopenharmony_ci		state = aa_dfa_match(policy->dfa, state, type);
24962306a36Sopenharmony_ci	state = aa_dfa_null_transition(policy->dfa, state);
25062306a36Sopenharmony_ci	if (!state)
25162306a36Sopenharmony_ci		return 3;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	state = match_mnt_flags(policy->dfa, state, flags);
25462306a36Sopenharmony_ci	if (!state)
25562306a36Sopenharmony_ci		return 4;
25662306a36Sopenharmony_ci	*perms = *aa_lookup_perms(policy, state);
25762306a36Sopenharmony_ci	if (perms->allow & AA_MAY_MOUNT)
25862306a36Sopenharmony_ci		return 0;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* only match data if not binary and the DFA flags data is expected */
26162306a36Sopenharmony_ci	if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) {
26262306a36Sopenharmony_ci		state = aa_dfa_null_transition(policy->dfa, state);
26362306a36Sopenharmony_ci		if (!state)
26462306a36Sopenharmony_ci			return 4;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		state = aa_dfa_match(policy->dfa, state, data);
26762306a36Sopenharmony_ci		if (!state)
26862306a36Sopenharmony_ci			return 5;
26962306a36Sopenharmony_ci		*perms = *aa_lookup_perms(policy, state);
27062306a36Sopenharmony_ci		if (perms->allow & AA_MAY_MOUNT)
27162306a36Sopenharmony_ci			return 0;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* failed at perms check, don't confuse with flags match */
27562306a36Sopenharmony_ci	return 6;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int path_flags(struct aa_profile *profile, const struct path *path)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	AA_BUG(!profile);
28262306a36Sopenharmony_ci	AA_BUG(!path);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return profile->path_flags |
28562306a36Sopenharmony_ci		(S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/**
28962306a36Sopenharmony_ci * match_mnt_path_str - handle path matching for mount
29062306a36Sopenharmony_ci * @subj_cred: cred of confined subject
29162306a36Sopenharmony_ci * @profile: the confining profile
29262306a36Sopenharmony_ci * @mntpath: for the mntpnt (NOT NULL)
29362306a36Sopenharmony_ci * @buffer: buffer to be used to lookup mntpath
29462306a36Sopenharmony_ci * @devname: string for the devname/src_name (MAY BE NULL OR ERRPTR)
29562306a36Sopenharmony_ci * @type: string for the dev type (MAYBE NULL)
29662306a36Sopenharmony_ci * @flags: mount flags to match
29762306a36Sopenharmony_ci * @data: fs mount data (MAYBE NULL)
29862306a36Sopenharmony_ci * @binary: whether @data is binary
29962306a36Sopenharmony_ci * @devinfo: error str if (IS_ERR(@devname))
30062306a36Sopenharmony_ci *
30162306a36Sopenharmony_ci * Returns: 0 on success else error
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_cistatic int match_mnt_path_str(const struct cred *subj_cred,
30462306a36Sopenharmony_ci			      struct aa_profile *profile,
30562306a36Sopenharmony_ci			      const struct path *mntpath, char *buffer,
30662306a36Sopenharmony_ci			      const char *devname, const char *type,
30762306a36Sopenharmony_ci			      unsigned long flags, void *data, bool binary,
30862306a36Sopenharmony_ci			      const char *devinfo)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct aa_perms perms = { };
31162306a36Sopenharmony_ci	const char *mntpnt = NULL, *info = NULL;
31262306a36Sopenharmony_ci	struct aa_ruleset *rules = list_first_entry(&profile->rules,
31362306a36Sopenharmony_ci						    typeof(*rules), list);
31462306a36Sopenharmony_ci	int pos, error;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	AA_BUG(!profile);
31762306a36Sopenharmony_ci	AA_BUG(!mntpath);
31862306a36Sopenharmony_ci	AA_BUG(!buffer);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
32162306a36Sopenharmony_ci		return 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
32462306a36Sopenharmony_ci			     &mntpnt, &info, profile->disconnected);
32562306a36Sopenharmony_ci	if (error)
32662306a36Sopenharmony_ci		goto audit;
32762306a36Sopenharmony_ci	if (IS_ERR(devname)) {
32862306a36Sopenharmony_ci		error = PTR_ERR(devname);
32962306a36Sopenharmony_ci		devname = NULL;
33062306a36Sopenharmony_ci		info = devinfo;
33162306a36Sopenharmony_ci		goto audit;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	error = -EACCES;
33562306a36Sopenharmony_ci	pos = do_match_mnt(&rules->policy,
33662306a36Sopenharmony_ci			   rules->policy.start[AA_CLASS_MOUNT],
33762306a36Sopenharmony_ci			   mntpnt, devname, type, flags, data, binary, &perms);
33862306a36Sopenharmony_ci	if (pos) {
33962306a36Sopenharmony_ci		info = mnt_info_table[pos];
34062306a36Sopenharmony_ci		goto audit;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci	error = 0;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ciaudit:
34562306a36Sopenharmony_ci	return audit_mount(subj_cred, profile, OP_MOUNT, mntpnt, devname,
34662306a36Sopenharmony_ci			   type, NULL,
34762306a36Sopenharmony_ci			   flags, data, AA_MAY_MOUNT, &perms, info, error);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/**
35162306a36Sopenharmony_ci * match_mnt - handle path matching for mount
35262306a36Sopenharmony_ci * @subj_cred: cred of the subject
35362306a36Sopenharmony_ci * @profile: the confining profile
35462306a36Sopenharmony_ci * @path: for the mntpnt (NOT NULL)
35562306a36Sopenharmony_ci * @buffer: buffer to be used to lookup mntpath
35662306a36Sopenharmony_ci * @devpath: path devname/src_name (MAYBE NULL)
35762306a36Sopenharmony_ci * @devbuffer: buffer to be used to lookup devname/src_name
35862306a36Sopenharmony_ci * @type: string for the dev type (MAYBE NULL)
35962306a36Sopenharmony_ci * @flags: mount flags to match
36062306a36Sopenharmony_ci * @data: fs mount data (MAYBE NULL)
36162306a36Sopenharmony_ci * @binary: whether @data is binary
36262306a36Sopenharmony_ci *
36362306a36Sopenharmony_ci * Returns: 0 on success else error
36462306a36Sopenharmony_ci */
36562306a36Sopenharmony_cistatic int match_mnt(const struct cred *subj_cred,
36662306a36Sopenharmony_ci		     struct aa_profile *profile, const struct path *path,
36762306a36Sopenharmony_ci		     char *buffer, const struct path *devpath, char *devbuffer,
36862306a36Sopenharmony_ci		     const char *type, unsigned long flags, void *data,
36962306a36Sopenharmony_ci		     bool binary)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	const char *devname = NULL, *info = NULL;
37262306a36Sopenharmony_ci	struct aa_ruleset *rules = list_first_entry(&profile->rules,
37362306a36Sopenharmony_ci						    typeof(*rules), list);
37462306a36Sopenharmony_ci	int error = -EACCES;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	AA_BUG(!profile);
37762306a36Sopenharmony_ci	AA_BUG(devpath && !devbuffer);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
38062306a36Sopenharmony_ci		return 0;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (devpath) {
38362306a36Sopenharmony_ci		error = aa_path_name(devpath, path_flags(profile, devpath),
38462306a36Sopenharmony_ci				     devbuffer, &devname, &info,
38562306a36Sopenharmony_ci				     profile->disconnected);
38662306a36Sopenharmony_ci		if (error)
38762306a36Sopenharmony_ci			devname = ERR_PTR(error);
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return match_mnt_path_str(subj_cred, profile, path, buffer, devname,
39162306a36Sopenharmony_ci				  type, flags, data, binary, info);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ciint aa_remount(const struct cred *subj_cred,
39562306a36Sopenharmony_ci	       struct aa_label *label, const struct path *path,
39662306a36Sopenharmony_ci	       unsigned long flags, void *data)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct aa_profile *profile;
39962306a36Sopenharmony_ci	char *buffer = NULL;
40062306a36Sopenharmony_ci	bool binary;
40162306a36Sopenharmony_ci	int error;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	AA_BUG(!label);
40462306a36Sopenharmony_ci	AA_BUG(!path);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	buffer = aa_get_buffer(false);
40962306a36Sopenharmony_ci	if (!buffer)
41062306a36Sopenharmony_ci		return -ENOMEM;
41162306a36Sopenharmony_ci	error = fn_for_each_confined(label, profile,
41262306a36Sopenharmony_ci			match_mnt(subj_cred, profile, path, buffer, NULL,
41362306a36Sopenharmony_ci				  NULL, NULL,
41462306a36Sopenharmony_ci				  flags, data, binary));
41562306a36Sopenharmony_ci	aa_put_buffer(buffer);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return error;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ciint aa_bind_mount(const struct cred *subj_cred,
42162306a36Sopenharmony_ci		  struct aa_label *label, const struct path *path,
42262306a36Sopenharmony_ci		  const char *dev_name, unsigned long flags)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct aa_profile *profile;
42562306a36Sopenharmony_ci	char *buffer = NULL, *old_buffer = NULL;
42662306a36Sopenharmony_ci	struct path old_path;
42762306a36Sopenharmony_ci	int error;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	AA_BUG(!label);
43062306a36Sopenharmony_ci	AA_BUG(!path);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (!dev_name || !*dev_name)
43362306a36Sopenharmony_ci		return -EINVAL;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	flags &= MS_REC | MS_BIND;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
43862306a36Sopenharmony_ci	if (error)
43962306a36Sopenharmony_ci		return error;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	buffer = aa_get_buffer(false);
44262306a36Sopenharmony_ci	old_buffer = aa_get_buffer(false);
44362306a36Sopenharmony_ci	error = -ENOMEM;
44462306a36Sopenharmony_ci	if (!buffer || !old_buffer)
44562306a36Sopenharmony_ci		goto out;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	error = fn_for_each_confined(label, profile,
44862306a36Sopenharmony_ci			match_mnt(subj_cred, profile, path, buffer, &old_path,
44962306a36Sopenharmony_ci				  old_buffer, NULL, flags, NULL, false));
45062306a36Sopenharmony_ciout:
45162306a36Sopenharmony_ci	aa_put_buffer(buffer);
45262306a36Sopenharmony_ci	aa_put_buffer(old_buffer);
45362306a36Sopenharmony_ci	path_put(&old_path);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return error;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ciint aa_mount_change_type(const struct cred *subj_cred,
45962306a36Sopenharmony_ci			 struct aa_label *label, const struct path *path,
46062306a36Sopenharmony_ci			 unsigned long flags)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct aa_profile *profile;
46362306a36Sopenharmony_ci	char *buffer = NULL;
46462306a36Sopenharmony_ci	int error;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	AA_BUG(!label);
46762306a36Sopenharmony_ci	AA_BUG(!path);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	/* These are the flags allowed by do_change_type() */
47062306a36Sopenharmony_ci	flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
47162306a36Sopenharmony_ci		  MS_UNBINDABLE);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	buffer = aa_get_buffer(false);
47462306a36Sopenharmony_ci	if (!buffer)
47562306a36Sopenharmony_ci		return -ENOMEM;
47662306a36Sopenharmony_ci	error = fn_for_each_confined(label, profile,
47762306a36Sopenharmony_ci			match_mnt(subj_cred, profile, path, buffer, NULL,
47862306a36Sopenharmony_ci				  NULL, NULL,
47962306a36Sopenharmony_ci				  flags, NULL, false));
48062306a36Sopenharmony_ci	aa_put_buffer(buffer);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return error;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ciint aa_move_mount(const struct cred *subj_cred,
48662306a36Sopenharmony_ci		  struct aa_label *label, const struct path *from_path,
48762306a36Sopenharmony_ci		  const struct path *to_path)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct aa_profile *profile;
49062306a36Sopenharmony_ci	char *to_buffer = NULL, *from_buffer = NULL;
49162306a36Sopenharmony_ci	int error;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	AA_BUG(!label);
49462306a36Sopenharmony_ci	AA_BUG(!from_path);
49562306a36Sopenharmony_ci	AA_BUG(!to_path);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	to_buffer = aa_get_buffer(false);
49862306a36Sopenharmony_ci	from_buffer = aa_get_buffer(false);
49962306a36Sopenharmony_ci	error = -ENOMEM;
50062306a36Sopenharmony_ci	if (!to_buffer || !from_buffer)
50162306a36Sopenharmony_ci		goto out;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (!our_mnt(from_path->mnt))
50462306a36Sopenharmony_ci		/* moving a mount detached from the namespace */
50562306a36Sopenharmony_ci		from_path = NULL;
50662306a36Sopenharmony_ci	error = fn_for_each_confined(label, profile,
50762306a36Sopenharmony_ci			match_mnt(subj_cred, profile, to_path, to_buffer,
50862306a36Sopenharmony_ci				  from_path, from_buffer,
50962306a36Sopenharmony_ci				  NULL, MS_MOVE, NULL, false));
51062306a36Sopenharmony_ciout:
51162306a36Sopenharmony_ci	aa_put_buffer(to_buffer);
51262306a36Sopenharmony_ci	aa_put_buffer(from_buffer);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return error;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ciint aa_move_mount_old(const struct cred *subj_cred, struct aa_label *label,
51862306a36Sopenharmony_ci		      const struct path *path, const char *orig_name)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct path old_path;
52162306a36Sopenharmony_ci	int error;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (!orig_name || !*orig_name)
52462306a36Sopenharmony_ci		return -EINVAL;
52562306a36Sopenharmony_ci	error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
52662306a36Sopenharmony_ci	if (error)
52762306a36Sopenharmony_ci		return error;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	error = aa_move_mount(subj_cred, label, &old_path, path);
53062306a36Sopenharmony_ci	path_put(&old_path);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	return error;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ciint aa_new_mount(const struct cred *subj_cred, struct aa_label *label,
53662306a36Sopenharmony_ci		 const char *dev_name, const struct path *path,
53762306a36Sopenharmony_ci		 const char *type, unsigned long flags, void *data)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct aa_profile *profile;
54062306a36Sopenharmony_ci	char *buffer = NULL, *dev_buffer = NULL;
54162306a36Sopenharmony_ci	bool binary = true;
54262306a36Sopenharmony_ci	int error;
54362306a36Sopenharmony_ci	int requires_dev = 0;
54462306a36Sopenharmony_ci	struct path tmp_path, *dev_path = NULL;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	AA_BUG(!label);
54762306a36Sopenharmony_ci	AA_BUG(!path);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (type) {
55062306a36Sopenharmony_ci		struct file_system_type *fstype;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		fstype = get_fs_type(type);
55362306a36Sopenharmony_ci		if (!fstype)
55462306a36Sopenharmony_ci			return -ENODEV;
55562306a36Sopenharmony_ci		binary = fstype->fs_flags & FS_BINARY_MOUNTDATA;
55662306a36Sopenharmony_ci		requires_dev = fstype->fs_flags & FS_REQUIRES_DEV;
55762306a36Sopenharmony_ci		put_filesystem(fstype);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		if (requires_dev) {
56062306a36Sopenharmony_ci			if (!dev_name || !*dev_name)
56162306a36Sopenharmony_ci				return -ENOENT;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci			error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path);
56462306a36Sopenharmony_ci			if (error)
56562306a36Sopenharmony_ci				return error;
56662306a36Sopenharmony_ci			dev_path = &tmp_path;
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	buffer = aa_get_buffer(false);
57162306a36Sopenharmony_ci	if (!buffer) {
57262306a36Sopenharmony_ci		error = -ENOMEM;
57362306a36Sopenharmony_ci		goto out;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci	if (dev_path) {
57662306a36Sopenharmony_ci		dev_buffer = aa_get_buffer(false);
57762306a36Sopenharmony_ci		if (!dev_buffer) {
57862306a36Sopenharmony_ci			error = -ENOMEM;
57962306a36Sopenharmony_ci			goto out;
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci		error = fn_for_each_confined(label, profile,
58262306a36Sopenharmony_ci				match_mnt(subj_cred, profile, path, buffer,
58362306a36Sopenharmony_ci					  dev_path, dev_buffer,
58462306a36Sopenharmony_ci				  type, flags, data, binary));
58562306a36Sopenharmony_ci	} else {
58662306a36Sopenharmony_ci		error = fn_for_each_confined(label, profile,
58762306a36Sopenharmony_ci				match_mnt_path_str(subj_cred, profile, path,
58862306a36Sopenharmony_ci					buffer, dev_name,
58962306a36Sopenharmony_ci					type, flags, data, binary, NULL));
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ciout:
59362306a36Sopenharmony_ci	aa_put_buffer(buffer);
59462306a36Sopenharmony_ci	aa_put_buffer(dev_buffer);
59562306a36Sopenharmony_ci	if (dev_path)
59662306a36Sopenharmony_ci		path_put(dev_path);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return error;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic int profile_umount(const struct cred *subj_cred,
60262306a36Sopenharmony_ci			  struct aa_profile *profile, const struct path *path,
60362306a36Sopenharmony_ci			  char *buffer)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct aa_ruleset *rules = list_first_entry(&profile->rules,
60662306a36Sopenharmony_ci						    typeof(*rules), list);
60762306a36Sopenharmony_ci	struct aa_perms perms = { };
60862306a36Sopenharmony_ci	const char *name = NULL, *info = NULL;
60962306a36Sopenharmony_ci	aa_state_t state;
61062306a36Sopenharmony_ci	int error;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	AA_BUG(!profile);
61362306a36Sopenharmony_ci	AA_BUG(!path);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
61662306a36Sopenharmony_ci		return 0;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	error = aa_path_name(path, path_flags(profile, path), buffer, &name,
61962306a36Sopenharmony_ci			     &info, profile->disconnected);
62062306a36Sopenharmony_ci	if (error)
62162306a36Sopenharmony_ci		goto audit;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	state = aa_dfa_match(rules->policy.dfa,
62462306a36Sopenharmony_ci			     rules->policy.start[AA_CLASS_MOUNT],
62562306a36Sopenharmony_ci			     name);
62662306a36Sopenharmony_ci	perms = *aa_lookup_perms(&rules->policy, state);
62762306a36Sopenharmony_ci	if (AA_MAY_UMOUNT & ~perms.allow)
62862306a36Sopenharmony_ci		error = -EACCES;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ciaudit:
63162306a36Sopenharmony_ci	return audit_mount(subj_cred, profile, OP_UMOUNT, name, NULL, NULL,
63262306a36Sopenharmony_ci			   NULL, 0, NULL,
63362306a36Sopenharmony_ci			   AA_MAY_UMOUNT, &perms, info, error);
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ciint aa_umount(const struct cred *subj_cred, struct aa_label *label,
63762306a36Sopenharmony_ci	      struct vfsmount *mnt, int flags)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct aa_profile *profile;
64062306a36Sopenharmony_ci	char *buffer = NULL;
64162306a36Sopenharmony_ci	int error;
64262306a36Sopenharmony_ci	struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	AA_BUG(!label);
64562306a36Sopenharmony_ci	AA_BUG(!mnt);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	buffer = aa_get_buffer(false);
64862306a36Sopenharmony_ci	if (!buffer)
64962306a36Sopenharmony_ci		return -ENOMEM;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	error = fn_for_each_confined(label, profile,
65262306a36Sopenharmony_ci			profile_umount(subj_cred, profile, &path, buffer));
65362306a36Sopenharmony_ci	aa_put_buffer(buffer);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return error;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci/* helper fn for transition on pivotroot
65962306a36Sopenharmony_ci *
66062306a36Sopenharmony_ci * Returns: label for transition or ERR_PTR. Does not return NULL
66162306a36Sopenharmony_ci */
66262306a36Sopenharmony_cistatic struct aa_label *build_pivotroot(const struct cred *subj_cred,
66362306a36Sopenharmony_ci					struct aa_profile *profile,
66462306a36Sopenharmony_ci					const struct path *new_path,
66562306a36Sopenharmony_ci					char *new_buffer,
66662306a36Sopenharmony_ci					const struct path *old_path,
66762306a36Sopenharmony_ci					char *old_buffer)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct aa_ruleset *rules = list_first_entry(&profile->rules,
67062306a36Sopenharmony_ci						    typeof(*rules), list);
67162306a36Sopenharmony_ci	const char *old_name, *new_name = NULL, *info = NULL;
67262306a36Sopenharmony_ci	const char *trans_name = NULL;
67362306a36Sopenharmony_ci	struct aa_perms perms = { };
67462306a36Sopenharmony_ci	aa_state_t state;
67562306a36Sopenharmony_ci	int error;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	AA_BUG(!profile);
67862306a36Sopenharmony_ci	AA_BUG(!new_path);
67962306a36Sopenharmony_ci	AA_BUG(!old_path);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (profile_unconfined(profile) ||
68262306a36Sopenharmony_ci	    !RULE_MEDIATES(rules, AA_CLASS_MOUNT))
68362306a36Sopenharmony_ci		return aa_get_newest_label(&profile->label);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	error = aa_path_name(old_path, path_flags(profile, old_path),
68662306a36Sopenharmony_ci			     old_buffer, &old_name, &info,
68762306a36Sopenharmony_ci			     profile->disconnected);
68862306a36Sopenharmony_ci	if (error)
68962306a36Sopenharmony_ci		goto audit;
69062306a36Sopenharmony_ci	error = aa_path_name(new_path, path_flags(profile, new_path),
69162306a36Sopenharmony_ci			     new_buffer, &new_name, &info,
69262306a36Sopenharmony_ci			     profile->disconnected);
69362306a36Sopenharmony_ci	if (error)
69462306a36Sopenharmony_ci		goto audit;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	error = -EACCES;
69762306a36Sopenharmony_ci	state = aa_dfa_match(rules->policy.dfa,
69862306a36Sopenharmony_ci			     rules->policy.start[AA_CLASS_MOUNT],
69962306a36Sopenharmony_ci			     new_name);
70062306a36Sopenharmony_ci	state = aa_dfa_null_transition(rules->policy.dfa, state);
70162306a36Sopenharmony_ci	state = aa_dfa_match(rules->policy.dfa, state, old_name);
70262306a36Sopenharmony_ci	perms = *aa_lookup_perms(&rules->policy, state);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (AA_MAY_PIVOTROOT & perms.allow)
70562306a36Sopenharmony_ci		error = 0;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ciaudit:
70862306a36Sopenharmony_ci	error = audit_mount(subj_cred, profile, OP_PIVOTROOT, new_name,
70962306a36Sopenharmony_ci			    old_name,
71062306a36Sopenharmony_ci			    NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT,
71162306a36Sopenharmony_ci			    &perms, info, error);
71262306a36Sopenharmony_ci	if (error)
71362306a36Sopenharmony_ci		return ERR_PTR(error);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	return aa_get_newest_label(&profile->label);
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ciint aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
71962306a36Sopenharmony_ci		 const struct path *old_path,
72062306a36Sopenharmony_ci		 const struct path *new_path)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct aa_profile *profile;
72362306a36Sopenharmony_ci	struct aa_label *target = NULL;
72462306a36Sopenharmony_ci	char *old_buffer = NULL, *new_buffer = NULL, *info = NULL;
72562306a36Sopenharmony_ci	int error;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	AA_BUG(!label);
72862306a36Sopenharmony_ci	AA_BUG(!old_path);
72962306a36Sopenharmony_ci	AA_BUG(!new_path);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	old_buffer = aa_get_buffer(false);
73262306a36Sopenharmony_ci	new_buffer = aa_get_buffer(false);
73362306a36Sopenharmony_ci	error = -ENOMEM;
73462306a36Sopenharmony_ci	if (!old_buffer || !new_buffer)
73562306a36Sopenharmony_ci		goto out;
73662306a36Sopenharmony_ci	target = fn_label_build(label, profile, GFP_KERNEL,
73762306a36Sopenharmony_ci			build_pivotroot(subj_cred, profile, new_path,
73862306a36Sopenharmony_ci					new_buffer,
73962306a36Sopenharmony_ci					old_path, old_buffer));
74062306a36Sopenharmony_ci	if (!target) {
74162306a36Sopenharmony_ci		info = "label build failed";
74262306a36Sopenharmony_ci		error = -ENOMEM;
74362306a36Sopenharmony_ci		goto fail;
74462306a36Sopenharmony_ci	} else if (!IS_ERR(target)) {
74562306a36Sopenharmony_ci		error = aa_replace_current_label(target);
74662306a36Sopenharmony_ci		if (error) {
74762306a36Sopenharmony_ci			/* TODO: audit target */
74862306a36Sopenharmony_ci			aa_put_label(target);
74962306a36Sopenharmony_ci			goto out;
75062306a36Sopenharmony_ci		}
75162306a36Sopenharmony_ci		aa_put_label(target);
75262306a36Sopenharmony_ci	} else
75362306a36Sopenharmony_ci		/* already audited error */
75462306a36Sopenharmony_ci		error = PTR_ERR(target);
75562306a36Sopenharmony_ciout:
75662306a36Sopenharmony_ci	aa_put_buffer(old_buffer);
75762306a36Sopenharmony_ci	aa_put_buffer(new_buffer);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	return error;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cifail:
76262306a36Sopenharmony_ci	/* TODO: add back in auditing of new_name and old_name */
76362306a36Sopenharmony_ci	error = fn_for_each(label, profile,
76462306a36Sopenharmony_ci			audit_mount(subj_cred, profile, OP_PIVOTROOT,
76562306a36Sopenharmony_ci				    NULL /*new_name */,
76662306a36Sopenharmony_ci				    NULL /* old_name */,
76762306a36Sopenharmony_ci				    NULL, NULL,
76862306a36Sopenharmony_ci				    0, NULL, AA_MAY_PIVOTROOT, &nullperms, info,
76962306a36Sopenharmony_ci				    error));
77062306a36Sopenharmony_ci	goto out;
77162306a36Sopenharmony_ci}
772