18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AppArmor security module
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file contains AppArmor policy attachment and domain transitions
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2002-2008 Novell/SUSE
88c2ecf20Sopenharmony_ci * Copyright 2009-2010 Canonical Ltd.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/fdtable.h>
138c2ecf20Sopenharmony_ci#include <linux/file.h>
148c2ecf20Sopenharmony_ci#include <linux/mount.h>
158c2ecf20Sopenharmony_ci#include <linux/syscalls.h>
168c2ecf20Sopenharmony_ci#include <linux/tracehook.h>
178c2ecf20Sopenharmony_ci#include <linux/personality.h>
188c2ecf20Sopenharmony_ci#include <linux/xattr.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "include/audit.h"
218c2ecf20Sopenharmony_ci#include "include/apparmorfs.h"
228c2ecf20Sopenharmony_ci#include "include/cred.h"
238c2ecf20Sopenharmony_ci#include "include/domain.h"
248c2ecf20Sopenharmony_ci#include "include/file.h"
258c2ecf20Sopenharmony_ci#include "include/ipc.h"
268c2ecf20Sopenharmony_ci#include "include/match.h"
278c2ecf20Sopenharmony_ci#include "include/path.h"
288c2ecf20Sopenharmony_ci#include "include/policy.h"
298c2ecf20Sopenharmony_ci#include "include/policy_ns.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/**
328c2ecf20Sopenharmony_ci * aa_free_domain_entries - free entries in a domain table
338c2ecf20Sopenharmony_ci * @domain: the domain table to free  (MAYBE NULL)
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_civoid aa_free_domain_entries(struct aa_domain *domain)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	int i;
388c2ecf20Sopenharmony_ci	if (domain) {
398c2ecf20Sopenharmony_ci		if (!domain->table)
408c2ecf20Sopenharmony_ci			return;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci		for (i = 0; i < domain->size; i++)
438c2ecf20Sopenharmony_ci			kfree_sensitive(domain->table[i]);
448c2ecf20Sopenharmony_ci		kfree_sensitive(domain->table);
458c2ecf20Sopenharmony_ci		domain->table = NULL;
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/**
508c2ecf20Sopenharmony_ci * may_change_ptraced_domain - check if can change profile on ptraced task
518c2ecf20Sopenharmony_ci * @to_label: profile to change to  (NOT NULL)
528c2ecf20Sopenharmony_ci * @info: message if there is an error
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * Check if current is ptraced and if so if the tracing task is allowed
558c2ecf20Sopenharmony_ci * to trace the new domain
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci * Returns: %0 or error if change not allowed
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_cistatic int may_change_ptraced_domain(struct aa_label *to_label,
608c2ecf20Sopenharmony_ci				     const char **info)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct task_struct *tracer;
638c2ecf20Sopenharmony_ci	struct aa_label *tracerl = NULL;
648c2ecf20Sopenharmony_ci	int error = 0;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	rcu_read_lock();
678c2ecf20Sopenharmony_ci	tracer = ptrace_parent(current);
688c2ecf20Sopenharmony_ci	if (tracer)
698c2ecf20Sopenharmony_ci		/* released below */
708c2ecf20Sopenharmony_ci		tracerl = aa_get_task_label(tracer);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* not ptraced */
738c2ecf20Sopenharmony_ci	if (!tracer || unconfined(tracerl))
748c2ecf20Sopenharmony_ci		goto out;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	error = aa_may_ptrace(tracerl, to_label, PTRACE_MODE_ATTACH);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ciout:
798c2ecf20Sopenharmony_ci	rcu_read_unlock();
808c2ecf20Sopenharmony_ci	aa_put_label(tracerl);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (error)
838c2ecf20Sopenharmony_ci		*info = "ptrace prevents transition";
848c2ecf20Sopenharmony_ci	return error;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/**** TODO: dedup to aa_label_match - needs perm and dfa, merging
888c2ecf20Sopenharmony_ci * specifically this is an exact copy of aa_label_match except
898c2ecf20Sopenharmony_ci * aa_compute_perms is replaced with aa_compute_fperms
908c2ecf20Sopenharmony_ci * and policy.dfa with file.dfa
918c2ecf20Sopenharmony_ci ****/
928c2ecf20Sopenharmony_ci/* match a profile and its associated ns component if needed
938c2ecf20Sopenharmony_ci * Assumes visibility test has already been done.
948c2ecf20Sopenharmony_ci * If a subns profile is not to be matched should be prescreened with
958c2ecf20Sopenharmony_ci * visibility test.
968c2ecf20Sopenharmony_ci */
978c2ecf20Sopenharmony_cistatic inline unsigned int match_component(struct aa_profile *profile,
988c2ecf20Sopenharmony_ci					   struct aa_profile *tp,
998c2ecf20Sopenharmony_ci					   bool stack, unsigned int state)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	const char *ns_name;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (stack)
1048c2ecf20Sopenharmony_ci		state = aa_dfa_match(profile->file.dfa, state, "&");
1058c2ecf20Sopenharmony_ci	if (profile->ns == tp->ns)
1068c2ecf20Sopenharmony_ci		return aa_dfa_match(profile->file.dfa, state, tp->base.hname);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* try matching with namespace name and then profile */
1098c2ecf20Sopenharmony_ci	ns_name = aa_ns_name(profile->ns, tp->ns, true);
1108c2ecf20Sopenharmony_ci	state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
1118c2ecf20Sopenharmony_ci	state = aa_dfa_match(profile->file.dfa, state, ns_name);
1128c2ecf20Sopenharmony_ci	state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
1138c2ecf20Sopenharmony_ci	return aa_dfa_match(profile->file.dfa, state, tp->base.hname);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/**
1178c2ecf20Sopenharmony_ci * label_compound_match - find perms for full compound label
1188c2ecf20Sopenharmony_ci * @profile: profile to find perms for
1198c2ecf20Sopenharmony_ci * @label: label to check access permissions for
1208c2ecf20Sopenharmony_ci * @stack: whether this is a stacking request
1218c2ecf20Sopenharmony_ci * @start: state to start match in
1228c2ecf20Sopenharmony_ci * @subns: whether to do permission checks on components in a subns
1238c2ecf20Sopenharmony_ci * @request: permissions to request
1248c2ecf20Sopenharmony_ci * @perms: perms struct to set
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * Returns: 0 on success else ERROR
1278c2ecf20Sopenharmony_ci *
1288c2ecf20Sopenharmony_ci * For the label A//&B//&C this does the perm match for A//&B//&C
1298c2ecf20Sopenharmony_ci * @perms should be preinitialized with allperms OR a previous permission
1308c2ecf20Sopenharmony_ci *        check to be stacked.
1318c2ecf20Sopenharmony_ci */
1328c2ecf20Sopenharmony_cistatic int label_compound_match(struct aa_profile *profile,
1338c2ecf20Sopenharmony_ci				struct aa_label *label, bool stack,
1348c2ecf20Sopenharmony_ci				unsigned int state, bool subns, u32 request,
1358c2ecf20Sopenharmony_ci				struct aa_perms *perms)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct aa_profile *tp;
1388c2ecf20Sopenharmony_ci	struct label_it i;
1398c2ecf20Sopenharmony_ci	struct path_cond cond = { };
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* find first subcomponent that is visible */
1428c2ecf20Sopenharmony_ci	label_for_each(i, label, tp) {
1438c2ecf20Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
1448c2ecf20Sopenharmony_ci			continue;
1458c2ecf20Sopenharmony_ci		state = match_component(profile, tp, stack, state);
1468c2ecf20Sopenharmony_ci		if (!state)
1478c2ecf20Sopenharmony_ci			goto fail;
1488c2ecf20Sopenharmony_ci		goto next;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* no component visible */
1528c2ecf20Sopenharmony_ci	*perms = allperms;
1538c2ecf20Sopenharmony_ci	return 0;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cinext:
1568c2ecf20Sopenharmony_ci	label_for_each_cont(i, label, tp) {
1578c2ecf20Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
1588c2ecf20Sopenharmony_ci			continue;
1598c2ecf20Sopenharmony_ci		state = aa_dfa_match(profile->file.dfa, state, "//&");
1608c2ecf20Sopenharmony_ci		state = match_component(profile, tp, false, state);
1618c2ecf20Sopenharmony_ci		if (!state)
1628c2ecf20Sopenharmony_ci			goto fail;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci	*perms = aa_compute_fperms(profile->file.dfa, state, &cond);
1658c2ecf20Sopenharmony_ci	aa_apply_modes_to_perms(profile, perms);
1668c2ecf20Sopenharmony_ci	if ((perms->allow & request) != request)
1678c2ecf20Sopenharmony_ci		return -EACCES;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return 0;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cifail:
1728c2ecf20Sopenharmony_ci	*perms = nullperms;
1738c2ecf20Sopenharmony_ci	return -EACCES;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/**
1778c2ecf20Sopenharmony_ci * label_components_match - find perms for all subcomponents of a label
1788c2ecf20Sopenharmony_ci * @profile: profile to find perms for
1798c2ecf20Sopenharmony_ci * @label: label to check access permissions for
1808c2ecf20Sopenharmony_ci * @stack: whether this is a stacking request
1818c2ecf20Sopenharmony_ci * @start: state to start match in
1828c2ecf20Sopenharmony_ci * @subns: whether to do permission checks on components in a subns
1838c2ecf20Sopenharmony_ci * @request: permissions to request
1848c2ecf20Sopenharmony_ci * @perms: an initialized perms struct to add accumulation to
1858c2ecf20Sopenharmony_ci *
1868c2ecf20Sopenharmony_ci * Returns: 0 on success else ERROR
1878c2ecf20Sopenharmony_ci *
1888c2ecf20Sopenharmony_ci * For the label A//&B//&C this does the perm match for each of A and B and C
1898c2ecf20Sopenharmony_ci * @perms should be preinitialized with allperms OR a previous permission
1908c2ecf20Sopenharmony_ci *        check to be stacked.
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_cistatic int label_components_match(struct aa_profile *profile,
1938c2ecf20Sopenharmony_ci				  struct aa_label *label, bool stack,
1948c2ecf20Sopenharmony_ci				  unsigned int start, bool subns, u32 request,
1958c2ecf20Sopenharmony_ci				  struct aa_perms *perms)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct aa_profile *tp;
1988c2ecf20Sopenharmony_ci	struct label_it i;
1998c2ecf20Sopenharmony_ci	struct aa_perms tmp;
2008c2ecf20Sopenharmony_ci	struct path_cond cond = { };
2018c2ecf20Sopenharmony_ci	unsigned int state = 0;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* find first subcomponent to test */
2048c2ecf20Sopenharmony_ci	label_for_each(i, label, tp) {
2058c2ecf20Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
2068c2ecf20Sopenharmony_ci			continue;
2078c2ecf20Sopenharmony_ci		state = match_component(profile, tp, stack, start);
2088c2ecf20Sopenharmony_ci		if (!state)
2098c2ecf20Sopenharmony_ci			goto fail;
2108c2ecf20Sopenharmony_ci		goto next;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* no subcomponents visible - no change in perms */
2148c2ecf20Sopenharmony_ci	return 0;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cinext:
2178c2ecf20Sopenharmony_ci	tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
2188c2ecf20Sopenharmony_ci	aa_apply_modes_to_perms(profile, &tmp);
2198c2ecf20Sopenharmony_ci	aa_perms_accum(perms, &tmp);
2208c2ecf20Sopenharmony_ci	label_for_each_cont(i, label, tp) {
2218c2ecf20Sopenharmony_ci		if (!aa_ns_visible(profile->ns, tp->ns, subns))
2228c2ecf20Sopenharmony_ci			continue;
2238c2ecf20Sopenharmony_ci		state = match_component(profile, tp, stack, start);
2248c2ecf20Sopenharmony_ci		if (!state)
2258c2ecf20Sopenharmony_ci			goto fail;
2268c2ecf20Sopenharmony_ci		tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
2278c2ecf20Sopenharmony_ci		aa_apply_modes_to_perms(profile, &tmp);
2288c2ecf20Sopenharmony_ci		aa_perms_accum(perms, &tmp);
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if ((perms->allow & request) != request)
2328c2ecf20Sopenharmony_ci		return -EACCES;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return 0;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cifail:
2378c2ecf20Sopenharmony_ci	*perms = nullperms;
2388c2ecf20Sopenharmony_ci	return -EACCES;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/**
2428c2ecf20Sopenharmony_ci * label_match - do a multi-component label match
2438c2ecf20Sopenharmony_ci * @profile: profile to match against (NOT NULL)
2448c2ecf20Sopenharmony_ci * @label: label to match (NOT NULL)
2458c2ecf20Sopenharmony_ci * @stack: whether this is a stacking request
2468c2ecf20Sopenharmony_ci * @state: state to start in
2478c2ecf20Sopenharmony_ci * @subns: whether to match subns components
2488c2ecf20Sopenharmony_ci * @request: permission request
2498c2ecf20Sopenharmony_ci * @perms: Returns computed perms (NOT NULL)
2508c2ecf20Sopenharmony_ci *
2518c2ecf20Sopenharmony_ci * Returns: the state the match finished in, may be the none matching state
2528c2ecf20Sopenharmony_ci */
2538c2ecf20Sopenharmony_cistatic int label_match(struct aa_profile *profile, struct aa_label *label,
2548c2ecf20Sopenharmony_ci		       bool stack, unsigned int state, bool subns, u32 request,
2558c2ecf20Sopenharmony_ci		       struct aa_perms *perms)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	int error;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	*perms = nullperms;
2608c2ecf20Sopenharmony_ci	error = label_compound_match(profile, label, stack, state, subns,
2618c2ecf20Sopenharmony_ci				     request, perms);
2628c2ecf20Sopenharmony_ci	if (!error)
2638c2ecf20Sopenharmony_ci		return error;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	*perms = allperms;
2668c2ecf20Sopenharmony_ci	return label_components_match(profile, label, stack, state, subns,
2678c2ecf20Sopenharmony_ci				      request, perms);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/******* end TODO: dedup *****/
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/**
2738c2ecf20Sopenharmony_ci * change_profile_perms - find permissions for change_profile
2748c2ecf20Sopenharmony_ci * @profile: the current profile  (NOT NULL)
2758c2ecf20Sopenharmony_ci * @target: label to transition to (NOT NULL)
2768c2ecf20Sopenharmony_ci * @stack: whether this is a stacking request
2778c2ecf20Sopenharmony_ci * @request: requested perms
2788c2ecf20Sopenharmony_ci * @start: state to start matching in
2798c2ecf20Sopenharmony_ci *
2808c2ecf20Sopenharmony_ci *
2818c2ecf20Sopenharmony_ci * Returns: permission set
2828c2ecf20Sopenharmony_ci *
2838c2ecf20Sopenharmony_ci * currently only matches full label A//&B//&C or individual components A, B, C
2848c2ecf20Sopenharmony_ci * not arbitrary combinations. Eg. A//&B, C
2858c2ecf20Sopenharmony_ci */
2868c2ecf20Sopenharmony_cistatic int change_profile_perms(struct aa_profile *profile,
2878c2ecf20Sopenharmony_ci				struct aa_label *target, bool stack,
2888c2ecf20Sopenharmony_ci				u32 request, unsigned int start,
2898c2ecf20Sopenharmony_ci				struct aa_perms *perms)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	if (profile_unconfined(profile)) {
2928c2ecf20Sopenharmony_ci		perms->allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC;
2938c2ecf20Sopenharmony_ci		perms->audit = perms->quiet = perms->kill = 0;
2948c2ecf20Sopenharmony_ci		return 0;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* TODO: add profile in ns screening */
2988c2ecf20Sopenharmony_ci	return label_match(profile, target, stack, start, true, request, perms);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/**
3028c2ecf20Sopenharmony_ci * aa_xattrs_match - check whether a file matches the xattrs defined in profile
3038c2ecf20Sopenharmony_ci * @bprm: binprm struct for the process to validate
3048c2ecf20Sopenharmony_ci * @profile: profile to match against (NOT NULL)
3058c2ecf20Sopenharmony_ci * @state: state to start match in
3068c2ecf20Sopenharmony_ci *
3078c2ecf20Sopenharmony_ci * Returns: number of extended attributes that matched, or < 0 on error
3088c2ecf20Sopenharmony_ci */
3098c2ecf20Sopenharmony_cistatic int aa_xattrs_match(const struct linux_binprm *bprm,
3108c2ecf20Sopenharmony_ci			   struct aa_profile *profile, unsigned int state)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	int i;
3138c2ecf20Sopenharmony_ci	ssize_t size;
3148c2ecf20Sopenharmony_ci	struct dentry *d;
3158c2ecf20Sopenharmony_ci	char *value = NULL;
3168c2ecf20Sopenharmony_ci	int value_size = 0, ret = profile->xattr_count;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (!bprm || !profile->xattr_count)
3198c2ecf20Sopenharmony_ci		return 0;
3208c2ecf20Sopenharmony_ci	might_sleep();
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* transition from exec match to xattr set */
3238c2ecf20Sopenharmony_ci	state = aa_dfa_outofband_transition(profile->xmatch, state);
3248c2ecf20Sopenharmony_ci	d = bprm->file->f_path.dentry;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	for (i = 0; i < profile->xattr_count; i++) {
3278c2ecf20Sopenharmony_ci		size = vfs_getxattr_alloc(d, profile->xattrs[i], &value,
3288c2ecf20Sopenharmony_ci					  value_size, GFP_KERNEL);
3298c2ecf20Sopenharmony_ci		if (size >= 0) {
3308c2ecf20Sopenharmony_ci			u32 perm;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci			/*
3338c2ecf20Sopenharmony_ci			 * Check the xattr presence before value. This ensure
3348c2ecf20Sopenharmony_ci			 * that not present xattr can be distinguished from a 0
3358c2ecf20Sopenharmony_ci			 * length value or rule that matches any value
3368c2ecf20Sopenharmony_ci			 */
3378c2ecf20Sopenharmony_ci			state = aa_dfa_null_transition(profile->xmatch, state);
3388c2ecf20Sopenharmony_ci			/* Check xattr value */
3398c2ecf20Sopenharmony_ci			state = aa_dfa_match_len(profile->xmatch, state, value,
3408c2ecf20Sopenharmony_ci						 size);
3418c2ecf20Sopenharmony_ci			perm = dfa_user_allow(profile->xmatch, state);
3428c2ecf20Sopenharmony_ci			if (!(perm & MAY_EXEC)) {
3438c2ecf20Sopenharmony_ci				ret = -EINVAL;
3448c2ecf20Sopenharmony_ci				goto out;
3458c2ecf20Sopenharmony_ci			}
3468c2ecf20Sopenharmony_ci		}
3478c2ecf20Sopenharmony_ci		/* transition to next element */
3488c2ecf20Sopenharmony_ci		state = aa_dfa_outofband_transition(profile->xmatch, state);
3498c2ecf20Sopenharmony_ci		if (size < 0) {
3508c2ecf20Sopenharmony_ci			/*
3518c2ecf20Sopenharmony_ci			 * No xattr match, so verify if transition to
3528c2ecf20Sopenharmony_ci			 * next element was valid. IFF so the xattr
3538c2ecf20Sopenharmony_ci			 * was optional.
3548c2ecf20Sopenharmony_ci			 */
3558c2ecf20Sopenharmony_ci			if (!state) {
3568c2ecf20Sopenharmony_ci				ret = -EINVAL;
3578c2ecf20Sopenharmony_ci				goto out;
3588c2ecf20Sopenharmony_ci			}
3598c2ecf20Sopenharmony_ci			/* don't count missing optional xattr as matched */
3608c2ecf20Sopenharmony_ci			ret--;
3618c2ecf20Sopenharmony_ci		}
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ciout:
3658c2ecf20Sopenharmony_ci	kfree(value);
3668c2ecf20Sopenharmony_ci	return ret;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci/**
3708c2ecf20Sopenharmony_ci * find_attach - do attachment search for unconfined processes
3718c2ecf20Sopenharmony_ci * @bprm - binprm structure of transitioning task
3728c2ecf20Sopenharmony_ci * @ns: the current namespace  (NOT NULL)
3738c2ecf20Sopenharmony_ci * @head - profile list to walk  (NOT NULL)
3748c2ecf20Sopenharmony_ci * @name - to match against  (NOT NULL)
3758c2ecf20Sopenharmony_ci * @info - info message if there was an error (NOT NULL)
3768c2ecf20Sopenharmony_ci *
3778c2ecf20Sopenharmony_ci * Do a linear search on the profiles in the list.  There is a matching
3788c2ecf20Sopenharmony_ci * preference where an exact match is preferred over a name which uses
3798c2ecf20Sopenharmony_ci * expressions to match, and matching expressions with the greatest
3808c2ecf20Sopenharmony_ci * xmatch_len are preferred.
3818c2ecf20Sopenharmony_ci *
3828c2ecf20Sopenharmony_ci * Requires: @head not be shared or have appropriate locks held
3838c2ecf20Sopenharmony_ci *
3848c2ecf20Sopenharmony_ci * Returns: label or NULL if no match found
3858c2ecf20Sopenharmony_ci */
3868c2ecf20Sopenharmony_cistatic struct aa_label *find_attach(const struct linux_binprm *bprm,
3878c2ecf20Sopenharmony_ci				    struct aa_ns *ns, struct list_head *head,
3888c2ecf20Sopenharmony_ci				    const char *name, const char **info)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	int candidate_len = 0, candidate_xattrs = 0;
3918c2ecf20Sopenharmony_ci	bool conflict = false;
3928c2ecf20Sopenharmony_ci	struct aa_profile *profile, *candidate = NULL;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	AA_BUG(!name);
3958c2ecf20Sopenharmony_ci	AA_BUG(!head);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	rcu_read_lock();
3988c2ecf20Sopenharmony_cirestart:
3998c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(profile, head, base.list) {
4008c2ecf20Sopenharmony_ci		if (profile->label.flags & FLAG_NULL &&
4018c2ecf20Sopenharmony_ci		    &profile->label == ns_unconfined(profile->ns))
4028c2ecf20Sopenharmony_ci			continue;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci		/* Find the "best" matching profile. Profiles must
4058c2ecf20Sopenharmony_ci		 * match the path and extended attributes (if any)
4068c2ecf20Sopenharmony_ci		 * associated with the file. A more specific path
4078c2ecf20Sopenharmony_ci		 * match will be preferred over a less specific one,
4088c2ecf20Sopenharmony_ci		 * and a match with more matching extended attributes
4098c2ecf20Sopenharmony_ci		 * will be preferred over one with fewer. If the best
4108c2ecf20Sopenharmony_ci		 * match has both the same level of path specificity
4118c2ecf20Sopenharmony_ci		 * and the same number of matching extended attributes
4128c2ecf20Sopenharmony_ci		 * as another profile, signal a conflict and refuse to
4138c2ecf20Sopenharmony_ci		 * match.
4148c2ecf20Sopenharmony_ci		 */
4158c2ecf20Sopenharmony_ci		if (profile->xmatch) {
4168c2ecf20Sopenharmony_ci			unsigned int state, count;
4178c2ecf20Sopenharmony_ci			u32 perm;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci			state = aa_dfa_leftmatch(profile->xmatch, DFA_START,
4208c2ecf20Sopenharmony_ci						 name, &count);
4218c2ecf20Sopenharmony_ci			perm = dfa_user_allow(profile->xmatch, state);
4228c2ecf20Sopenharmony_ci			/* any accepting state means a valid match. */
4238c2ecf20Sopenharmony_ci			if (perm & MAY_EXEC) {
4248c2ecf20Sopenharmony_ci				int ret = 0;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci				if (count < candidate_len)
4278c2ecf20Sopenharmony_ci					continue;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci				if (bprm && profile->xattr_count) {
4308c2ecf20Sopenharmony_ci					long rev = READ_ONCE(ns->revision);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci					if (!aa_get_profile_not0(profile))
4338c2ecf20Sopenharmony_ci						goto restart;
4348c2ecf20Sopenharmony_ci					rcu_read_unlock();
4358c2ecf20Sopenharmony_ci					ret = aa_xattrs_match(bprm, profile,
4368c2ecf20Sopenharmony_ci							      state);
4378c2ecf20Sopenharmony_ci					rcu_read_lock();
4388c2ecf20Sopenharmony_ci					aa_put_profile(profile);
4398c2ecf20Sopenharmony_ci					if (rev !=
4408c2ecf20Sopenharmony_ci					    READ_ONCE(ns->revision))
4418c2ecf20Sopenharmony_ci						/* policy changed */
4428c2ecf20Sopenharmony_ci						goto restart;
4438c2ecf20Sopenharmony_ci					/*
4448c2ecf20Sopenharmony_ci					 * Fail matching if the xattrs don't
4458c2ecf20Sopenharmony_ci					 * match
4468c2ecf20Sopenharmony_ci					 */
4478c2ecf20Sopenharmony_ci					if (ret < 0)
4488c2ecf20Sopenharmony_ci						continue;
4498c2ecf20Sopenharmony_ci				}
4508c2ecf20Sopenharmony_ci				/*
4518c2ecf20Sopenharmony_ci				 * TODO: allow for more flexible best match
4528c2ecf20Sopenharmony_ci				 *
4538c2ecf20Sopenharmony_ci				 * The new match isn't more specific
4548c2ecf20Sopenharmony_ci				 * than the current best match
4558c2ecf20Sopenharmony_ci				 */
4568c2ecf20Sopenharmony_ci				if (count == candidate_len &&
4578c2ecf20Sopenharmony_ci				    ret <= candidate_xattrs) {
4588c2ecf20Sopenharmony_ci					/* Match is equivalent, so conflict */
4598c2ecf20Sopenharmony_ci					if (ret == candidate_xattrs)
4608c2ecf20Sopenharmony_ci						conflict = true;
4618c2ecf20Sopenharmony_ci					continue;
4628c2ecf20Sopenharmony_ci				}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci				/* Either the same length with more matching
4658c2ecf20Sopenharmony_ci				 * xattrs, or a longer match
4668c2ecf20Sopenharmony_ci				 */
4678c2ecf20Sopenharmony_ci				candidate = profile;
4688c2ecf20Sopenharmony_ci				candidate_len = max(count, profile->xmatch_len);
4698c2ecf20Sopenharmony_ci				candidate_xattrs = ret;
4708c2ecf20Sopenharmony_ci				conflict = false;
4718c2ecf20Sopenharmony_ci			}
4728c2ecf20Sopenharmony_ci		} else if (!strcmp(profile->base.name, name)) {
4738c2ecf20Sopenharmony_ci			/*
4748c2ecf20Sopenharmony_ci			 * old exact non-re match, without conditionals such
4758c2ecf20Sopenharmony_ci			 * as xattrs. no more searching required
4768c2ecf20Sopenharmony_ci			 */
4778c2ecf20Sopenharmony_ci			candidate = profile;
4788c2ecf20Sopenharmony_ci			goto out;
4798c2ecf20Sopenharmony_ci		}
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (!candidate || conflict) {
4838c2ecf20Sopenharmony_ci		if (conflict)
4848c2ecf20Sopenharmony_ci			*info = "conflicting profile attachments";
4858c2ecf20Sopenharmony_ci		rcu_read_unlock();
4868c2ecf20Sopenharmony_ci		return NULL;
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ciout:
4908c2ecf20Sopenharmony_ci	candidate = aa_get_newest_profile(candidate);
4918c2ecf20Sopenharmony_ci	rcu_read_unlock();
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	return &candidate->label;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic const char *next_name(int xtype, const char *name)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	return NULL;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci/**
5028c2ecf20Sopenharmony_ci * x_table_lookup - lookup an x transition name via transition table
5038c2ecf20Sopenharmony_ci * @profile: current profile (NOT NULL)
5048c2ecf20Sopenharmony_ci * @xindex: index into x transition table
5058c2ecf20Sopenharmony_ci * @name: returns: name tested to find label (NOT NULL)
5068c2ecf20Sopenharmony_ci *
5078c2ecf20Sopenharmony_ci * Returns: refcounted label, or NULL on failure (MAYBE NULL)
5088c2ecf20Sopenharmony_ci */
5098c2ecf20Sopenharmony_cistruct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
5108c2ecf20Sopenharmony_ci				const char **name)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct aa_label *label = NULL;
5138c2ecf20Sopenharmony_ci	u32 xtype = xindex & AA_X_TYPE_MASK;
5148c2ecf20Sopenharmony_ci	int index = xindex & AA_X_INDEX_MASK;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	AA_BUG(!name);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/* index is guaranteed to be in range, validated at load time */
5198c2ecf20Sopenharmony_ci	/* TODO: move lookup parsing to unpack time so this is a straight
5208c2ecf20Sopenharmony_ci	 *       index into the resultant label
5218c2ecf20Sopenharmony_ci	 */
5228c2ecf20Sopenharmony_ci	for (*name = profile->file.trans.table[index]; !label && *name;
5238c2ecf20Sopenharmony_ci	     *name = next_name(xtype, *name)) {
5248c2ecf20Sopenharmony_ci		if (xindex & AA_X_CHILD) {
5258c2ecf20Sopenharmony_ci			struct aa_profile *new_profile;
5268c2ecf20Sopenharmony_ci			/* release by caller */
5278c2ecf20Sopenharmony_ci			new_profile = aa_find_child(profile, *name);
5288c2ecf20Sopenharmony_ci			if (new_profile)
5298c2ecf20Sopenharmony_ci				label = &new_profile->label;
5308c2ecf20Sopenharmony_ci			continue;
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci		label = aa_label_parse(&profile->label, *name, GFP_KERNEL,
5338c2ecf20Sopenharmony_ci				       true, false);
5348c2ecf20Sopenharmony_ci		if (IS_ERR(label))
5358c2ecf20Sopenharmony_ci			label = NULL;
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	/* released by caller */
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	return label;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci/**
5448c2ecf20Sopenharmony_ci * x_to_label - get target label for a given xindex
5458c2ecf20Sopenharmony_ci * @profile: current profile  (NOT NULL)
5468c2ecf20Sopenharmony_ci * @bprm: binprm structure of transitioning task
5478c2ecf20Sopenharmony_ci * @name: name to lookup (NOT NULL)
5488c2ecf20Sopenharmony_ci * @xindex: index into x transition table
5498c2ecf20Sopenharmony_ci * @lookupname: returns: name used in lookup if one was specified (NOT NULL)
5508c2ecf20Sopenharmony_ci *
5518c2ecf20Sopenharmony_ci * find label for a transition index
5528c2ecf20Sopenharmony_ci *
5538c2ecf20Sopenharmony_ci * Returns: refcounted label or NULL if not found available
5548c2ecf20Sopenharmony_ci */
5558c2ecf20Sopenharmony_cistatic struct aa_label *x_to_label(struct aa_profile *profile,
5568c2ecf20Sopenharmony_ci				   const struct linux_binprm *bprm,
5578c2ecf20Sopenharmony_ci				   const char *name, u32 xindex,
5588c2ecf20Sopenharmony_ci				   const char **lookupname,
5598c2ecf20Sopenharmony_ci				   const char **info)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct aa_label *new = NULL;
5628c2ecf20Sopenharmony_ci	struct aa_ns *ns = profile->ns;
5638c2ecf20Sopenharmony_ci	u32 xtype = xindex & AA_X_TYPE_MASK;
5648c2ecf20Sopenharmony_ci	const char *stack = NULL;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	switch (xtype) {
5678c2ecf20Sopenharmony_ci	case AA_X_NONE:
5688c2ecf20Sopenharmony_ci		/* fail exec unless ix || ux fallback - handled by caller */
5698c2ecf20Sopenharmony_ci		*lookupname = NULL;
5708c2ecf20Sopenharmony_ci		break;
5718c2ecf20Sopenharmony_ci	case AA_X_TABLE:
5728c2ecf20Sopenharmony_ci		/* TODO: fix when perm mapping done at unload */
5738c2ecf20Sopenharmony_ci		stack = profile->file.trans.table[xindex & AA_X_INDEX_MASK];
5748c2ecf20Sopenharmony_ci		if (*stack != '&') {
5758c2ecf20Sopenharmony_ci			/* released by caller */
5768c2ecf20Sopenharmony_ci			new = x_table_lookup(profile, xindex, lookupname);
5778c2ecf20Sopenharmony_ci			stack = NULL;
5788c2ecf20Sopenharmony_ci			break;
5798c2ecf20Sopenharmony_ci		}
5808c2ecf20Sopenharmony_ci		fallthrough;	/* to X_NAME */
5818c2ecf20Sopenharmony_ci	case AA_X_NAME:
5828c2ecf20Sopenharmony_ci		if (xindex & AA_X_CHILD)
5838c2ecf20Sopenharmony_ci			/* released by caller */
5848c2ecf20Sopenharmony_ci			new = find_attach(bprm, ns, &profile->base.profiles,
5858c2ecf20Sopenharmony_ci					  name, info);
5868c2ecf20Sopenharmony_ci		else
5878c2ecf20Sopenharmony_ci			/* released by caller */
5888c2ecf20Sopenharmony_ci			new = find_attach(bprm, ns, &ns->base.profiles,
5898c2ecf20Sopenharmony_ci					  name, info);
5908c2ecf20Sopenharmony_ci		*lookupname = name;
5918c2ecf20Sopenharmony_ci		break;
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (!new) {
5958c2ecf20Sopenharmony_ci		if (xindex & AA_X_INHERIT) {
5968c2ecf20Sopenharmony_ci			/* (p|c|n)ix - don't change profile but do
5978c2ecf20Sopenharmony_ci			 * use the newest version
5988c2ecf20Sopenharmony_ci			 */
5998c2ecf20Sopenharmony_ci			*info = "ix fallback";
6008c2ecf20Sopenharmony_ci			/* no profile && no error */
6018c2ecf20Sopenharmony_ci			new = aa_get_newest_label(&profile->label);
6028c2ecf20Sopenharmony_ci		} else if (xindex & AA_X_UNCONFINED) {
6038c2ecf20Sopenharmony_ci			new = aa_get_newest_label(ns_unconfined(profile->ns));
6048c2ecf20Sopenharmony_ci			*info = "ux fallback";
6058c2ecf20Sopenharmony_ci		}
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	if (new && stack) {
6098c2ecf20Sopenharmony_ci		/* base the stack on post domain transition */
6108c2ecf20Sopenharmony_ci		struct aa_label *base = new;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci		new = aa_label_parse(base, stack, GFP_KERNEL, true, false);
6138c2ecf20Sopenharmony_ci		if (IS_ERR(new))
6148c2ecf20Sopenharmony_ci			new = NULL;
6158c2ecf20Sopenharmony_ci		aa_put_label(base);
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	/* released by caller */
6198c2ecf20Sopenharmony_ci	return new;
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cistatic struct aa_label *profile_transition(struct aa_profile *profile,
6238c2ecf20Sopenharmony_ci					   const struct linux_binprm *bprm,
6248c2ecf20Sopenharmony_ci					   char *buffer, struct path_cond *cond,
6258c2ecf20Sopenharmony_ci					   bool *secure_exec)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct aa_label *new = NULL;
6288c2ecf20Sopenharmony_ci	const char *info = NULL, *name = NULL, *target = NULL;
6298c2ecf20Sopenharmony_ci	unsigned int state = profile->file.start;
6308c2ecf20Sopenharmony_ci	struct aa_perms perms = {};
6318c2ecf20Sopenharmony_ci	bool nonewprivs = false;
6328c2ecf20Sopenharmony_ci	int error = 0;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	AA_BUG(!profile);
6358c2ecf20Sopenharmony_ci	AA_BUG(!bprm);
6368c2ecf20Sopenharmony_ci	AA_BUG(!buffer);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
6398c2ecf20Sopenharmony_ci			     &name, &info, profile->disconnected);
6408c2ecf20Sopenharmony_ci	if (error) {
6418c2ecf20Sopenharmony_ci		if (profile_unconfined(profile) ||
6428c2ecf20Sopenharmony_ci		    (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
6438c2ecf20Sopenharmony_ci			AA_DEBUG("name lookup ix on error");
6448c2ecf20Sopenharmony_ci			error = 0;
6458c2ecf20Sopenharmony_ci			new = aa_get_newest_label(&profile->label);
6468c2ecf20Sopenharmony_ci		}
6478c2ecf20Sopenharmony_ci		name = bprm->filename;
6488c2ecf20Sopenharmony_ci		goto audit;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (profile_unconfined(profile)) {
6528c2ecf20Sopenharmony_ci		new = find_attach(bprm, profile->ns,
6538c2ecf20Sopenharmony_ci				  &profile->ns->base.profiles, name, &info);
6548c2ecf20Sopenharmony_ci		if (new) {
6558c2ecf20Sopenharmony_ci			AA_DEBUG("unconfined attached to new label");
6568c2ecf20Sopenharmony_ci			return new;
6578c2ecf20Sopenharmony_ci		}
6588c2ecf20Sopenharmony_ci		AA_DEBUG("unconfined exec no attachment");
6598c2ecf20Sopenharmony_ci		return aa_get_newest_label(&profile->label);
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* find exec permissions for name */
6638c2ecf20Sopenharmony_ci	state = aa_str_perms(profile->file.dfa, state, name, cond, &perms);
6648c2ecf20Sopenharmony_ci	if (perms.allow & MAY_EXEC) {
6658c2ecf20Sopenharmony_ci		/* exec permission determine how to transition */
6668c2ecf20Sopenharmony_ci		new = x_to_label(profile, bprm, name, perms.xindex, &target,
6678c2ecf20Sopenharmony_ci				 &info);
6688c2ecf20Sopenharmony_ci		if (new && new->proxy == profile->label.proxy && info) {
6698c2ecf20Sopenharmony_ci			/* hack ix fallback - improve how this is detected */
6708c2ecf20Sopenharmony_ci			goto audit;
6718c2ecf20Sopenharmony_ci		} else if (!new) {
6728c2ecf20Sopenharmony_ci			error = -EACCES;
6738c2ecf20Sopenharmony_ci			info = "profile transition not found";
6748c2ecf20Sopenharmony_ci			/* remove MAY_EXEC to audit as failure */
6758c2ecf20Sopenharmony_ci			perms.allow &= ~MAY_EXEC;
6768c2ecf20Sopenharmony_ci		}
6778c2ecf20Sopenharmony_ci	} else if (COMPLAIN_MODE(profile)) {
6788c2ecf20Sopenharmony_ci		/* no exec permission - learning mode */
6798c2ecf20Sopenharmony_ci		struct aa_profile *new_profile = NULL;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci		new_profile = aa_new_null_profile(profile, false, name,
6828c2ecf20Sopenharmony_ci						  GFP_KERNEL);
6838c2ecf20Sopenharmony_ci		if (!new_profile) {
6848c2ecf20Sopenharmony_ci			error = -ENOMEM;
6858c2ecf20Sopenharmony_ci			info = "could not create null profile";
6868c2ecf20Sopenharmony_ci		} else {
6878c2ecf20Sopenharmony_ci			error = -EACCES;
6888c2ecf20Sopenharmony_ci			new = &new_profile->label;
6898c2ecf20Sopenharmony_ci		}
6908c2ecf20Sopenharmony_ci		perms.xindex |= AA_X_UNSAFE;
6918c2ecf20Sopenharmony_ci	} else
6928c2ecf20Sopenharmony_ci		/* fail exec */
6938c2ecf20Sopenharmony_ci		error = -EACCES;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (!new)
6968c2ecf20Sopenharmony_ci		goto audit;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	if (!(perms.xindex & AA_X_UNSAFE)) {
7008c2ecf20Sopenharmony_ci		if (DEBUG_ON) {
7018c2ecf20Sopenharmony_ci			dbg_printk("apparmor: scrubbing environment variables"
7028c2ecf20Sopenharmony_ci				   " for %s profile=", name);
7038c2ecf20Sopenharmony_ci			aa_label_printk(new, GFP_KERNEL);
7048c2ecf20Sopenharmony_ci			dbg_printk("\n");
7058c2ecf20Sopenharmony_ci		}
7068c2ecf20Sopenharmony_ci		*secure_exec = true;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ciaudit:
7108c2ecf20Sopenharmony_ci	aa_audit_file(profile, &perms, OP_EXEC, MAY_EXEC, name, target, new,
7118c2ecf20Sopenharmony_ci		      cond->uid, info, error);
7128c2ecf20Sopenharmony_ci	if (!new || nonewprivs) {
7138c2ecf20Sopenharmony_ci		aa_put_label(new);
7148c2ecf20Sopenharmony_ci		return ERR_PTR(error);
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	return new;
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
7218c2ecf20Sopenharmony_ci			  bool stack, const struct linux_binprm *bprm,
7228c2ecf20Sopenharmony_ci			  char *buffer, struct path_cond *cond,
7238c2ecf20Sopenharmony_ci			  bool *secure_exec)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	unsigned int state = profile->file.start;
7268c2ecf20Sopenharmony_ci	struct aa_perms perms = {};
7278c2ecf20Sopenharmony_ci	const char *xname = NULL, *info = "change_profile onexec";
7288c2ecf20Sopenharmony_ci	int error = -EACCES;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	AA_BUG(!profile);
7318c2ecf20Sopenharmony_ci	AA_BUG(!onexec);
7328c2ecf20Sopenharmony_ci	AA_BUG(!bprm);
7338c2ecf20Sopenharmony_ci	AA_BUG(!buffer);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	if (profile_unconfined(profile)) {
7368c2ecf20Sopenharmony_ci		/* change_profile on exec already granted */
7378c2ecf20Sopenharmony_ci		/*
7388c2ecf20Sopenharmony_ci		 * NOTE: Domain transitions from unconfined are allowed
7398c2ecf20Sopenharmony_ci		 * even when no_new_privs is set because this aways results
7408c2ecf20Sopenharmony_ci		 * in a further reduction of permissions.
7418c2ecf20Sopenharmony_ci		 */
7428c2ecf20Sopenharmony_ci		return 0;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
7468c2ecf20Sopenharmony_ci			     &xname, &info, profile->disconnected);
7478c2ecf20Sopenharmony_ci	if (error) {
7488c2ecf20Sopenharmony_ci		if (profile_unconfined(profile) ||
7498c2ecf20Sopenharmony_ci		    (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
7508c2ecf20Sopenharmony_ci			AA_DEBUG("name lookup ix on error");
7518c2ecf20Sopenharmony_ci			error = 0;
7528c2ecf20Sopenharmony_ci		}
7538c2ecf20Sopenharmony_ci		xname = bprm->filename;
7548c2ecf20Sopenharmony_ci		goto audit;
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/* find exec permissions for name */
7588c2ecf20Sopenharmony_ci	state = aa_str_perms(profile->file.dfa, state, xname, cond, &perms);
7598c2ecf20Sopenharmony_ci	if (!(perms.allow & AA_MAY_ONEXEC)) {
7608c2ecf20Sopenharmony_ci		info = "no change_onexec valid for executable";
7618c2ecf20Sopenharmony_ci		goto audit;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci	/* test if this exec can be paired with change_profile onexec.
7648c2ecf20Sopenharmony_ci	 * onexec permission is linked to exec with a standard pairing
7658c2ecf20Sopenharmony_ci	 * exec\0change_profile
7668c2ecf20Sopenharmony_ci	 */
7678c2ecf20Sopenharmony_ci	state = aa_dfa_null_transition(profile->file.dfa, state);
7688c2ecf20Sopenharmony_ci	error = change_profile_perms(profile, onexec, stack, AA_MAY_ONEXEC,
7698c2ecf20Sopenharmony_ci				     state, &perms);
7708c2ecf20Sopenharmony_ci	if (error) {
7718c2ecf20Sopenharmony_ci		perms.allow &= ~AA_MAY_ONEXEC;
7728c2ecf20Sopenharmony_ci		goto audit;
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	if (!(perms.xindex & AA_X_UNSAFE)) {
7768c2ecf20Sopenharmony_ci		if (DEBUG_ON) {
7778c2ecf20Sopenharmony_ci			dbg_printk("apparmor: scrubbing environment "
7788c2ecf20Sopenharmony_ci				   "variables for %s label=", xname);
7798c2ecf20Sopenharmony_ci			aa_label_printk(onexec, GFP_KERNEL);
7808c2ecf20Sopenharmony_ci			dbg_printk("\n");
7818c2ecf20Sopenharmony_ci		}
7828c2ecf20Sopenharmony_ci		*secure_exec = true;
7838c2ecf20Sopenharmony_ci	}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ciaudit:
7868c2ecf20Sopenharmony_ci	return aa_audit_file(profile, &perms, OP_EXEC, AA_MAY_ONEXEC, xname,
7878c2ecf20Sopenharmony_ci			     NULL, onexec, cond->uid, info, error);
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci/* ensure none ns domain transitions are correctly applied with onexec */
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic struct aa_label *handle_onexec(struct aa_label *label,
7938c2ecf20Sopenharmony_ci				      struct aa_label *onexec, bool stack,
7948c2ecf20Sopenharmony_ci				      const struct linux_binprm *bprm,
7958c2ecf20Sopenharmony_ci				      char *buffer, struct path_cond *cond,
7968c2ecf20Sopenharmony_ci				      bool *unsafe)
7978c2ecf20Sopenharmony_ci{
7988c2ecf20Sopenharmony_ci	struct aa_profile *profile;
7998c2ecf20Sopenharmony_ci	struct aa_label *new;
8008c2ecf20Sopenharmony_ci	int error;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	AA_BUG(!label);
8038c2ecf20Sopenharmony_ci	AA_BUG(!onexec);
8048c2ecf20Sopenharmony_ci	AA_BUG(!bprm);
8058c2ecf20Sopenharmony_ci	AA_BUG(!buffer);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (!stack) {
8088c2ecf20Sopenharmony_ci		error = fn_for_each_in_ns(label, profile,
8098c2ecf20Sopenharmony_ci				profile_onexec(profile, onexec, stack,
8108c2ecf20Sopenharmony_ci					       bprm, buffer, cond, unsafe));
8118c2ecf20Sopenharmony_ci		if (error)
8128c2ecf20Sopenharmony_ci			return ERR_PTR(error);
8138c2ecf20Sopenharmony_ci		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
8148c2ecf20Sopenharmony_ci				aa_get_newest_label(onexec),
8158c2ecf20Sopenharmony_ci				profile_transition(profile, bprm, buffer,
8168c2ecf20Sopenharmony_ci						   cond, unsafe));
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	} else {
8198c2ecf20Sopenharmony_ci		/* TODO: determine how much we want to loosen this */
8208c2ecf20Sopenharmony_ci		error = fn_for_each_in_ns(label, profile,
8218c2ecf20Sopenharmony_ci				profile_onexec(profile, onexec, stack, bprm,
8228c2ecf20Sopenharmony_ci					       buffer, cond, unsafe));
8238c2ecf20Sopenharmony_ci		if (error)
8248c2ecf20Sopenharmony_ci			return ERR_PTR(error);
8258c2ecf20Sopenharmony_ci		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
8268c2ecf20Sopenharmony_ci				aa_label_merge(&profile->label, onexec,
8278c2ecf20Sopenharmony_ci					       GFP_KERNEL),
8288c2ecf20Sopenharmony_ci				profile_transition(profile, bprm, buffer,
8298c2ecf20Sopenharmony_ci						   cond, unsafe));
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if (new)
8338c2ecf20Sopenharmony_ci		return new;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	/* TODO: get rid of GLOBAL_ROOT_UID */
8368c2ecf20Sopenharmony_ci	error = fn_for_each_in_ns(label, profile,
8378c2ecf20Sopenharmony_ci			aa_audit_file(profile, &nullperms, OP_CHANGE_ONEXEC,
8388c2ecf20Sopenharmony_ci				      AA_MAY_ONEXEC, bprm->filename, NULL,
8398c2ecf20Sopenharmony_ci				      onexec, GLOBAL_ROOT_UID,
8408c2ecf20Sopenharmony_ci				      "failed to build target label", -ENOMEM));
8418c2ecf20Sopenharmony_ci	return ERR_PTR(error);
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci/**
8458c2ecf20Sopenharmony_ci * apparmor_bprm_creds_for_exec - Update the new creds on the bprm struct
8468c2ecf20Sopenharmony_ci * @bprm: binprm for the exec  (NOT NULL)
8478c2ecf20Sopenharmony_ci *
8488c2ecf20Sopenharmony_ci * Returns: %0 or error on failure
8498c2ecf20Sopenharmony_ci *
8508c2ecf20Sopenharmony_ci * TODO: once the other paths are done see if we can't refactor into a fn
8518c2ecf20Sopenharmony_ci */
8528c2ecf20Sopenharmony_ciint apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	struct aa_task_ctx *ctx;
8558c2ecf20Sopenharmony_ci	struct aa_label *label, *new = NULL;
8568c2ecf20Sopenharmony_ci	struct aa_profile *profile;
8578c2ecf20Sopenharmony_ci	char *buffer = NULL;
8588c2ecf20Sopenharmony_ci	const char *info = NULL;
8598c2ecf20Sopenharmony_ci	int error = 0;
8608c2ecf20Sopenharmony_ci	bool unsafe = false;
8618c2ecf20Sopenharmony_ci	struct path_cond cond = {
8628c2ecf20Sopenharmony_ci		file_inode(bprm->file)->i_uid,
8638c2ecf20Sopenharmony_ci		file_inode(bprm->file)->i_mode
8648c2ecf20Sopenharmony_ci	};
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	ctx = task_ctx(current);
8678c2ecf20Sopenharmony_ci	AA_BUG(!cred_label(bprm->cred));
8688c2ecf20Sopenharmony_ci	AA_BUG(!ctx);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	label = aa_get_newest_label(cred_label(bprm->cred));
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	/*
8738c2ecf20Sopenharmony_ci	 * Detect no new privs being set, and store the label it
8748c2ecf20Sopenharmony_ci	 * occurred under. Ideally this would happen when nnp
8758c2ecf20Sopenharmony_ci	 * is set but there isn't a good way to do that yet.
8768c2ecf20Sopenharmony_ci	 *
8778c2ecf20Sopenharmony_ci	 * Testing for unconfined must be done before the subset test
8788c2ecf20Sopenharmony_ci	 */
8798c2ecf20Sopenharmony_ci	if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && !unconfined(label) &&
8808c2ecf20Sopenharmony_ci	    !ctx->nnp)
8818c2ecf20Sopenharmony_ci		ctx->nnp = aa_get_label(label);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* buffer freed below, name is pointer into buffer */
8848c2ecf20Sopenharmony_ci	buffer = aa_get_buffer(false);
8858c2ecf20Sopenharmony_ci	if (!buffer) {
8868c2ecf20Sopenharmony_ci		error = -ENOMEM;
8878c2ecf20Sopenharmony_ci		goto done;
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	/* Test for onexec first as onexec override other x transitions. */
8918c2ecf20Sopenharmony_ci	if (ctx->onexec)
8928c2ecf20Sopenharmony_ci		new = handle_onexec(label, ctx->onexec, ctx->token,
8938c2ecf20Sopenharmony_ci				    bprm, buffer, &cond, &unsafe);
8948c2ecf20Sopenharmony_ci	else
8958c2ecf20Sopenharmony_ci		new = fn_label_build(label, profile, GFP_KERNEL,
8968c2ecf20Sopenharmony_ci				profile_transition(profile, bprm, buffer,
8978c2ecf20Sopenharmony_ci						   &cond, &unsafe));
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	AA_BUG(!new);
9008c2ecf20Sopenharmony_ci	if (IS_ERR(new)) {
9018c2ecf20Sopenharmony_ci		error = PTR_ERR(new);
9028c2ecf20Sopenharmony_ci		goto done;
9038c2ecf20Sopenharmony_ci	} else if (!new) {
9048c2ecf20Sopenharmony_ci		error = -ENOMEM;
9058c2ecf20Sopenharmony_ci		goto done;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	/* Policy has specified a domain transitions. If no_new_privs and
9098c2ecf20Sopenharmony_ci	 * confined ensure the transition is to confinement that is subset
9108c2ecf20Sopenharmony_ci	 * of the confinement when the task entered no new privs.
9118c2ecf20Sopenharmony_ci	 *
9128c2ecf20Sopenharmony_ci	 * NOTE: Domain transitions from unconfined and to stacked
9138c2ecf20Sopenharmony_ci	 * subsets are allowed even when no_new_privs is set because this
9148c2ecf20Sopenharmony_ci	 * aways results in a further reduction of permissions.
9158c2ecf20Sopenharmony_ci	 */
9168c2ecf20Sopenharmony_ci	if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) &&
9178c2ecf20Sopenharmony_ci	    !unconfined(label) &&
9188c2ecf20Sopenharmony_ci	    !aa_label_is_unconfined_subset(new, ctx->nnp)) {
9198c2ecf20Sopenharmony_ci		error = -EPERM;
9208c2ecf20Sopenharmony_ci		info = "no new privs";
9218c2ecf20Sopenharmony_ci		goto audit;
9228c2ecf20Sopenharmony_ci	}
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if (bprm->unsafe & LSM_UNSAFE_SHARE) {
9258c2ecf20Sopenharmony_ci		/* FIXME: currently don't mediate shared state */
9268c2ecf20Sopenharmony_ci		;
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	if (bprm->unsafe & (LSM_UNSAFE_PTRACE)) {
9308c2ecf20Sopenharmony_ci		/* TODO: test needs to be profile of label to new */
9318c2ecf20Sopenharmony_ci		error = may_change_ptraced_domain(new, &info);
9328c2ecf20Sopenharmony_ci		if (error)
9338c2ecf20Sopenharmony_ci			goto audit;
9348c2ecf20Sopenharmony_ci	}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	if (unsafe) {
9378c2ecf20Sopenharmony_ci		if (DEBUG_ON) {
9388c2ecf20Sopenharmony_ci			dbg_printk("scrubbing environment variables for %s "
9398c2ecf20Sopenharmony_ci				   "label=", bprm->filename);
9408c2ecf20Sopenharmony_ci			aa_label_printk(new, GFP_KERNEL);
9418c2ecf20Sopenharmony_ci			dbg_printk("\n");
9428c2ecf20Sopenharmony_ci		}
9438c2ecf20Sopenharmony_ci		bprm->secureexec = 1;
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (label->proxy != new->proxy) {
9478c2ecf20Sopenharmony_ci		/* when transitioning clear unsafe personality bits */
9488c2ecf20Sopenharmony_ci		if (DEBUG_ON) {
9498c2ecf20Sopenharmony_ci			dbg_printk("apparmor: clearing unsafe personality "
9508c2ecf20Sopenharmony_ci				   "bits. %s label=", bprm->filename);
9518c2ecf20Sopenharmony_ci			aa_label_printk(new, GFP_KERNEL);
9528c2ecf20Sopenharmony_ci			dbg_printk("\n");
9538c2ecf20Sopenharmony_ci		}
9548c2ecf20Sopenharmony_ci		bprm->per_clear |= PER_CLEAR_ON_SETID;
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci	aa_put_label(cred_label(bprm->cred));
9578c2ecf20Sopenharmony_ci	/* transfer reference, released when cred is freed */
9588c2ecf20Sopenharmony_ci	set_cred_label(bprm->cred, new);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cidone:
9618c2ecf20Sopenharmony_ci	aa_put_label(label);
9628c2ecf20Sopenharmony_ci	aa_put_buffer(buffer);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	return error;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ciaudit:
9678c2ecf20Sopenharmony_ci	error = fn_for_each(label, profile,
9688c2ecf20Sopenharmony_ci			aa_audit_file(profile, &nullperms, OP_EXEC, MAY_EXEC,
9698c2ecf20Sopenharmony_ci				      bprm->filename, NULL, new,
9708c2ecf20Sopenharmony_ci				      file_inode(bprm->file)->i_uid, info,
9718c2ecf20Sopenharmony_ci				      error));
9728c2ecf20Sopenharmony_ci	aa_put_label(new);
9738c2ecf20Sopenharmony_ci	goto done;
9748c2ecf20Sopenharmony_ci}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci/*
9778c2ecf20Sopenharmony_ci * Functions for self directed profile change
9788c2ecf20Sopenharmony_ci */
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci/* helper fn for change_hat
9828c2ecf20Sopenharmony_ci *
9838c2ecf20Sopenharmony_ci * Returns: label for hat transition OR ERR_PTR.  Does NOT return NULL
9848c2ecf20Sopenharmony_ci */
9858c2ecf20Sopenharmony_cistatic struct aa_label *build_change_hat(struct aa_profile *profile,
9868c2ecf20Sopenharmony_ci					 const char *name, bool sibling)
9878c2ecf20Sopenharmony_ci{
9888c2ecf20Sopenharmony_ci	struct aa_profile *root, *hat = NULL;
9898c2ecf20Sopenharmony_ci	const char *info = NULL;
9908c2ecf20Sopenharmony_ci	int error = 0;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	if (sibling && PROFILE_IS_HAT(profile)) {
9938c2ecf20Sopenharmony_ci		root = aa_get_profile_rcu(&profile->parent);
9948c2ecf20Sopenharmony_ci	} else if (!sibling && !PROFILE_IS_HAT(profile)) {
9958c2ecf20Sopenharmony_ci		root = aa_get_profile(profile);
9968c2ecf20Sopenharmony_ci	} else {
9978c2ecf20Sopenharmony_ci		info = "conflicting target types";
9988c2ecf20Sopenharmony_ci		error = -EPERM;
9998c2ecf20Sopenharmony_ci		goto audit;
10008c2ecf20Sopenharmony_ci	}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	hat = aa_find_child(root, name);
10038c2ecf20Sopenharmony_ci	if (!hat) {
10048c2ecf20Sopenharmony_ci		error = -ENOENT;
10058c2ecf20Sopenharmony_ci		if (COMPLAIN_MODE(profile)) {
10068c2ecf20Sopenharmony_ci			hat = aa_new_null_profile(profile, true, name,
10078c2ecf20Sopenharmony_ci						  GFP_KERNEL);
10088c2ecf20Sopenharmony_ci			if (!hat) {
10098c2ecf20Sopenharmony_ci				info = "failed null profile create";
10108c2ecf20Sopenharmony_ci				error = -ENOMEM;
10118c2ecf20Sopenharmony_ci			}
10128c2ecf20Sopenharmony_ci		}
10138c2ecf20Sopenharmony_ci	}
10148c2ecf20Sopenharmony_ci	aa_put_profile(root);
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ciaudit:
10178c2ecf20Sopenharmony_ci	aa_audit_file(profile, &nullperms, OP_CHANGE_HAT, AA_MAY_CHANGEHAT,
10188c2ecf20Sopenharmony_ci		      name, hat ? hat->base.hname : NULL,
10198c2ecf20Sopenharmony_ci		      hat ? &hat->label : NULL, GLOBAL_ROOT_UID, info,
10208c2ecf20Sopenharmony_ci		      error);
10218c2ecf20Sopenharmony_ci	if (!hat || (error && error != -ENOENT))
10228c2ecf20Sopenharmony_ci		return ERR_PTR(error);
10238c2ecf20Sopenharmony_ci	/* if hat && error - complain mode, already audited and we adjust for
10248c2ecf20Sopenharmony_ci	 * complain mode allow by returning hat->label
10258c2ecf20Sopenharmony_ci	 */
10268c2ecf20Sopenharmony_ci	return &hat->label;
10278c2ecf20Sopenharmony_ci}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci/* helper fn for changing into a hat
10308c2ecf20Sopenharmony_ci *
10318c2ecf20Sopenharmony_ci * Returns: label for hat transition or ERR_PTR. Does not return NULL
10328c2ecf20Sopenharmony_ci */
10338c2ecf20Sopenharmony_cistatic struct aa_label *change_hat(struct aa_label *label, const char *hats[],
10348c2ecf20Sopenharmony_ci				   int count, int flags)
10358c2ecf20Sopenharmony_ci{
10368c2ecf20Sopenharmony_ci	struct aa_profile *profile, *root, *hat = NULL;
10378c2ecf20Sopenharmony_ci	struct aa_label *new;
10388c2ecf20Sopenharmony_ci	struct label_it it;
10398c2ecf20Sopenharmony_ci	bool sibling = false;
10408c2ecf20Sopenharmony_ci	const char *name, *info = NULL;
10418c2ecf20Sopenharmony_ci	int i, error;
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	AA_BUG(!label);
10448c2ecf20Sopenharmony_ci	AA_BUG(!hats);
10458c2ecf20Sopenharmony_ci	AA_BUG(count < 1);
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	if (PROFILE_IS_HAT(labels_profile(label)))
10488c2ecf20Sopenharmony_ci		sibling = true;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	/*find first matching hat */
10518c2ecf20Sopenharmony_ci	for (i = 0; i < count && !hat; i++) {
10528c2ecf20Sopenharmony_ci		name = hats[i];
10538c2ecf20Sopenharmony_ci		label_for_each_in_ns(it, labels_ns(label), label, profile) {
10548c2ecf20Sopenharmony_ci			if (sibling && PROFILE_IS_HAT(profile)) {
10558c2ecf20Sopenharmony_ci				root = aa_get_profile_rcu(&profile->parent);
10568c2ecf20Sopenharmony_ci			} else if (!sibling && !PROFILE_IS_HAT(profile)) {
10578c2ecf20Sopenharmony_ci				root = aa_get_profile(profile);
10588c2ecf20Sopenharmony_ci			} else {	/* conflicting change type */
10598c2ecf20Sopenharmony_ci				info = "conflicting targets types";
10608c2ecf20Sopenharmony_ci				error = -EPERM;
10618c2ecf20Sopenharmony_ci				goto fail;
10628c2ecf20Sopenharmony_ci			}
10638c2ecf20Sopenharmony_ci			hat = aa_find_child(root, name);
10648c2ecf20Sopenharmony_ci			aa_put_profile(root);
10658c2ecf20Sopenharmony_ci			if (!hat) {
10668c2ecf20Sopenharmony_ci				if (!COMPLAIN_MODE(profile))
10678c2ecf20Sopenharmony_ci					goto outer_continue;
10688c2ecf20Sopenharmony_ci				/* complain mode succeed as if hat */
10698c2ecf20Sopenharmony_ci			} else if (!PROFILE_IS_HAT(hat)) {
10708c2ecf20Sopenharmony_ci				info = "target not hat";
10718c2ecf20Sopenharmony_ci				error = -EPERM;
10728c2ecf20Sopenharmony_ci				aa_put_profile(hat);
10738c2ecf20Sopenharmony_ci				goto fail;
10748c2ecf20Sopenharmony_ci			}
10758c2ecf20Sopenharmony_ci			aa_put_profile(hat);
10768c2ecf20Sopenharmony_ci		}
10778c2ecf20Sopenharmony_ci		/* found a hat for all profiles in ns */
10788c2ecf20Sopenharmony_ci		goto build;
10798c2ecf20Sopenharmony_ciouter_continue:
10808c2ecf20Sopenharmony_ci	;
10818c2ecf20Sopenharmony_ci	}
10828c2ecf20Sopenharmony_ci	/* no hats that match, find appropriate error
10838c2ecf20Sopenharmony_ci	 *
10848c2ecf20Sopenharmony_ci	 * In complain mode audit of the failure is based off of the first
10858c2ecf20Sopenharmony_ci	 * hat supplied.  This is done due how userspace interacts with
10868c2ecf20Sopenharmony_ci	 * change_hat.
10878c2ecf20Sopenharmony_ci	 */
10888c2ecf20Sopenharmony_ci	name = NULL;
10898c2ecf20Sopenharmony_ci	label_for_each_in_ns(it, labels_ns(label), label, profile) {
10908c2ecf20Sopenharmony_ci		if (!list_empty(&profile->base.profiles)) {
10918c2ecf20Sopenharmony_ci			info = "hat not found";
10928c2ecf20Sopenharmony_ci			error = -ENOENT;
10938c2ecf20Sopenharmony_ci			goto fail;
10948c2ecf20Sopenharmony_ci		}
10958c2ecf20Sopenharmony_ci	}
10968c2ecf20Sopenharmony_ci	info = "no hats defined";
10978c2ecf20Sopenharmony_ci	error = -ECHILD;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_cifail:
11008c2ecf20Sopenharmony_ci	label_for_each_in_ns(it, labels_ns(label), label, profile) {
11018c2ecf20Sopenharmony_ci		/*
11028c2ecf20Sopenharmony_ci		 * no target as it has failed to be found or built
11038c2ecf20Sopenharmony_ci		 *
11048c2ecf20Sopenharmony_ci		 * change_hat uses probing and should not log failures
11058c2ecf20Sopenharmony_ci		 * related to missing hats
11068c2ecf20Sopenharmony_ci		 */
11078c2ecf20Sopenharmony_ci		/* TODO: get rid of GLOBAL_ROOT_UID */
11088c2ecf20Sopenharmony_ci		if (count > 1 || COMPLAIN_MODE(profile)) {
11098c2ecf20Sopenharmony_ci			aa_audit_file(profile, &nullperms, OP_CHANGE_HAT,
11108c2ecf20Sopenharmony_ci				      AA_MAY_CHANGEHAT, name, NULL, NULL,
11118c2ecf20Sopenharmony_ci				      GLOBAL_ROOT_UID, info, error);
11128c2ecf20Sopenharmony_ci		}
11138c2ecf20Sopenharmony_ci	}
11148c2ecf20Sopenharmony_ci	return ERR_PTR(error);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_cibuild:
11178c2ecf20Sopenharmony_ci	new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
11188c2ecf20Sopenharmony_ci				   build_change_hat(profile, name, sibling),
11198c2ecf20Sopenharmony_ci				   aa_get_label(&profile->label));
11208c2ecf20Sopenharmony_ci	if (!new) {
11218c2ecf20Sopenharmony_ci		info = "label build failed";
11228c2ecf20Sopenharmony_ci		error = -ENOMEM;
11238c2ecf20Sopenharmony_ci		goto fail;
11248c2ecf20Sopenharmony_ci	} /* else if (IS_ERR) build_change_hat has logged error so return new */
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	return new;
11278c2ecf20Sopenharmony_ci}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci/**
11308c2ecf20Sopenharmony_ci * aa_change_hat - change hat to/from subprofile
11318c2ecf20Sopenharmony_ci * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0)
11328c2ecf20Sopenharmony_ci * @count: number of hat names in @hats
11338c2ecf20Sopenharmony_ci * @token: magic value to validate the hat change
11348c2ecf20Sopenharmony_ci * @flags: flags affecting behavior of the change
11358c2ecf20Sopenharmony_ci *
11368c2ecf20Sopenharmony_ci * Returns %0 on success, error otherwise.
11378c2ecf20Sopenharmony_ci *
11388c2ecf20Sopenharmony_ci * Change to the first profile specified in @hats that exists, and store
11398c2ecf20Sopenharmony_ci * the @hat_magic in the current task context.  If the count == 0 and the
11408c2ecf20Sopenharmony_ci * @token matches that stored in the current task context, return to the
11418c2ecf20Sopenharmony_ci * top level profile.
11428c2ecf20Sopenharmony_ci *
11438c2ecf20Sopenharmony_ci * change_hat only applies to profiles in the current ns, and each profile
11448c2ecf20Sopenharmony_ci * in the ns must make the same transition otherwise change_hat will fail.
11458c2ecf20Sopenharmony_ci */
11468c2ecf20Sopenharmony_ciint aa_change_hat(const char *hats[], int count, u64 token, int flags)
11478c2ecf20Sopenharmony_ci{
11488c2ecf20Sopenharmony_ci	const struct cred *cred;
11498c2ecf20Sopenharmony_ci	struct aa_task_ctx *ctx = task_ctx(current);
11508c2ecf20Sopenharmony_ci	struct aa_label *label, *previous, *new = NULL, *target = NULL;
11518c2ecf20Sopenharmony_ci	struct aa_profile *profile;
11528c2ecf20Sopenharmony_ci	struct aa_perms perms = {};
11538c2ecf20Sopenharmony_ci	const char *info = NULL;
11548c2ecf20Sopenharmony_ci	int error = 0;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	/* released below */
11578c2ecf20Sopenharmony_ci	cred = get_current_cred();
11588c2ecf20Sopenharmony_ci	label = aa_get_newest_cred_label(cred);
11598c2ecf20Sopenharmony_ci	previous = aa_get_newest_label(ctx->previous);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	/*
11628c2ecf20Sopenharmony_ci	 * Detect no new privs being set, and store the label it
11638c2ecf20Sopenharmony_ci	 * occurred under. Ideally this would happen when nnp
11648c2ecf20Sopenharmony_ci	 * is set but there isn't a good way to do that yet.
11658c2ecf20Sopenharmony_ci	 *
11668c2ecf20Sopenharmony_ci	 * Testing for unconfined must be done before the subset test
11678c2ecf20Sopenharmony_ci	 */
11688c2ecf20Sopenharmony_ci	if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
11698c2ecf20Sopenharmony_ci		ctx->nnp = aa_get_label(label);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	if (unconfined(label)) {
11728c2ecf20Sopenharmony_ci		info = "unconfined can not change_hat";
11738c2ecf20Sopenharmony_ci		error = -EPERM;
11748c2ecf20Sopenharmony_ci		goto fail;
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	if (count) {
11788c2ecf20Sopenharmony_ci		new = change_hat(label, hats, count, flags);
11798c2ecf20Sopenharmony_ci		AA_BUG(!new);
11808c2ecf20Sopenharmony_ci		if (IS_ERR(new)) {
11818c2ecf20Sopenharmony_ci			error = PTR_ERR(new);
11828c2ecf20Sopenharmony_ci			new = NULL;
11838c2ecf20Sopenharmony_ci			/* already audited */
11848c2ecf20Sopenharmony_ci			goto out;
11858c2ecf20Sopenharmony_ci		}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		error = may_change_ptraced_domain(new, &info);
11888c2ecf20Sopenharmony_ci		if (error)
11898c2ecf20Sopenharmony_ci			goto fail;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci		/*
11928c2ecf20Sopenharmony_ci		 * no new privs prevents domain transitions that would
11938c2ecf20Sopenharmony_ci		 * reduce restrictions.
11948c2ecf20Sopenharmony_ci		 */
11958c2ecf20Sopenharmony_ci		if (task_no_new_privs(current) && !unconfined(label) &&
11968c2ecf20Sopenharmony_ci		    !aa_label_is_unconfined_subset(new, ctx->nnp)) {
11978c2ecf20Sopenharmony_ci			/* not an apparmor denial per se, so don't log it */
11988c2ecf20Sopenharmony_ci			AA_DEBUG("no_new_privs - change_hat denied");
11998c2ecf20Sopenharmony_ci			error = -EPERM;
12008c2ecf20Sopenharmony_ci			goto out;
12018c2ecf20Sopenharmony_ci		}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci		if (flags & AA_CHANGE_TEST)
12048c2ecf20Sopenharmony_ci			goto out;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci		target = new;
12078c2ecf20Sopenharmony_ci		error = aa_set_current_hat(new, token);
12088c2ecf20Sopenharmony_ci		if (error == -EACCES)
12098c2ecf20Sopenharmony_ci			/* kill task in case of brute force attacks */
12108c2ecf20Sopenharmony_ci			goto kill;
12118c2ecf20Sopenharmony_ci	} else if (previous && !(flags & AA_CHANGE_TEST)) {
12128c2ecf20Sopenharmony_ci		/*
12138c2ecf20Sopenharmony_ci		 * no new privs prevents domain transitions that would
12148c2ecf20Sopenharmony_ci		 * reduce restrictions.
12158c2ecf20Sopenharmony_ci		 */
12168c2ecf20Sopenharmony_ci		if (task_no_new_privs(current) && !unconfined(label) &&
12178c2ecf20Sopenharmony_ci		    !aa_label_is_unconfined_subset(previous, ctx->nnp)) {
12188c2ecf20Sopenharmony_ci			/* not an apparmor denial per se, so don't log it */
12198c2ecf20Sopenharmony_ci			AA_DEBUG("no_new_privs - change_hat denied");
12208c2ecf20Sopenharmony_ci			error = -EPERM;
12218c2ecf20Sopenharmony_ci			goto out;
12228c2ecf20Sopenharmony_ci		}
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci		/* Return to saved label.  Kill task if restore fails
12258c2ecf20Sopenharmony_ci		 * to avoid brute force attacks
12268c2ecf20Sopenharmony_ci		 */
12278c2ecf20Sopenharmony_ci		target = previous;
12288c2ecf20Sopenharmony_ci		error = aa_restore_previous_label(token);
12298c2ecf20Sopenharmony_ci		if (error) {
12308c2ecf20Sopenharmony_ci			if (error == -EACCES)
12318c2ecf20Sopenharmony_ci				goto kill;
12328c2ecf20Sopenharmony_ci			goto fail;
12338c2ecf20Sopenharmony_ci		}
12348c2ecf20Sopenharmony_ci	} /* else ignore @flags && restores when there is no saved profile */
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ciout:
12378c2ecf20Sopenharmony_ci	aa_put_label(new);
12388c2ecf20Sopenharmony_ci	aa_put_label(previous);
12398c2ecf20Sopenharmony_ci	aa_put_label(label);
12408c2ecf20Sopenharmony_ci	put_cred(cred);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	return error;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cikill:
12458c2ecf20Sopenharmony_ci	info = "failed token match";
12468c2ecf20Sopenharmony_ci	perms.kill = AA_MAY_CHANGEHAT;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_cifail:
12498c2ecf20Sopenharmony_ci	fn_for_each_in_ns(label, profile,
12508c2ecf20Sopenharmony_ci		aa_audit_file(profile, &perms, OP_CHANGE_HAT,
12518c2ecf20Sopenharmony_ci			      AA_MAY_CHANGEHAT, NULL, NULL, target,
12528c2ecf20Sopenharmony_ci			      GLOBAL_ROOT_UID, info, error));
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	goto out;
12558c2ecf20Sopenharmony_ci}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_cistatic int change_profile_perms_wrapper(const char *op, const char *name,
12598c2ecf20Sopenharmony_ci					struct aa_profile *profile,
12608c2ecf20Sopenharmony_ci					struct aa_label *target, bool stack,
12618c2ecf20Sopenharmony_ci					u32 request, struct aa_perms *perms)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	const char *info = NULL;
12648c2ecf20Sopenharmony_ci	int error = 0;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	if (!error)
12678c2ecf20Sopenharmony_ci		error = change_profile_perms(profile, target, stack, request,
12688c2ecf20Sopenharmony_ci					     profile->file.start, perms);
12698c2ecf20Sopenharmony_ci	if (error)
12708c2ecf20Sopenharmony_ci		error = aa_audit_file(profile, perms, op, request, name,
12718c2ecf20Sopenharmony_ci				      NULL, target, GLOBAL_ROOT_UID, info,
12728c2ecf20Sopenharmony_ci				      error);
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	return error;
12758c2ecf20Sopenharmony_ci}
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci/**
12788c2ecf20Sopenharmony_ci * aa_change_profile - perform a one-way profile transition
12798c2ecf20Sopenharmony_ci * @fqname: name of profile may include namespace (NOT NULL)
12808c2ecf20Sopenharmony_ci * @onexec: whether this transition is to take place immediately or at exec
12818c2ecf20Sopenharmony_ci * @flags: flags affecting change behavior
12828c2ecf20Sopenharmony_ci *
12838c2ecf20Sopenharmony_ci * Change to new profile @name.  Unlike with hats, there is no way
12848c2ecf20Sopenharmony_ci * to change back.  If @name isn't specified the current profile name is
12858c2ecf20Sopenharmony_ci * used.
12868c2ecf20Sopenharmony_ci * If @onexec then the transition is delayed until
12878c2ecf20Sopenharmony_ci * the next exec.
12888c2ecf20Sopenharmony_ci *
12898c2ecf20Sopenharmony_ci * Returns %0 on success, error otherwise.
12908c2ecf20Sopenharmony_ci */
12918c2ecf20Sopenharmony_ciint aa_change_profile(const char *fqname, int flags)
12928c2ecf20Sopenharmony_ci{
12938c2ecf20Sopenharmony_ci	struct aa_label *label, *new = NULL, *target = NULL;
12948c2ecf20Sopenharmony_ci	struct aa_profile *profile;
12958c2ecf20Sopenharmony_ci	struct aa_perms perms = {};
12968c2ecf20Sopenharmony_ci	const char *info = NULL;
12978c2ecf20Sopenharmony_ci	const char *auditname = fqname;		/* retain leading & if stack */
12988c2ecf20Sopenharmony_ci	bool stack = flags & AA_CHANGE_STACK;
12998c2ecf20Sopenharmony_ci	struct aa_task_ctx *ctx = task_ctx(current);
13008c2ecf20Sopenharmony_ci	int error = 0;
13018c2ecf20Sopenharmony_ci	char *op;
13028c2ecf20Sopenharmony_ci	u32 request;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	label = aa_get_current_label();
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	/*
13078c2ecf20Sopenharmony_ci	 * Detect no new privs being set, and store the label it
13088c2ecf20Sopenharmony_ci	 * occurred under. Ideally this would happen when nnp
13098c2ecf20Sopenharmony_ci	 * is set but there isn't a good way to do that yet.
13108c2ecf20Sopenharmony_ci	 *
13118c2ecf20Sopenharmony_ci	 * Testing for unconfined must be done before the subset test
13128c2ecf20Sopenharmony_ci	 */
13138c2ecf20Sopenharmony_ci	if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
13148c2ecf20Sopenharmony_ci		ctx->nnp = aa_get_label(label);
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	if (!fqname || !*fqname) {
13178c2ecf20Sopenharmony_ci		aa_put_label(label);
13188c2ecf20Sopenharmony_ci		AA_DEBUG("no profile name");
13198c2ecf20Sopenharmony_ci		return -EINVAL;
13208c2ecf20Sopenharmony_ci	}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	if (flags & AA_CHANGE_ONEXEC) {
13238c2ecf20Sopenharmony_ci		request = AA_MAY_ONEXEC;
13248c2ecf20Sopenharmony_ci		if (stack)
13258c2ecf20Sopenharmony_ci			op = OP_STACK_ONEXEC;
13268c2ecf20Sopenharmony_ci		else
13278c2ecf20Sopenharmony_ci			op = OP_CHANGE_ONEXEC;
13288c2ecf20Sopenharmony_ci	} else {
13298c2ecf20Sopenharmony_ci		request = AA_MAY_CHANGE_PROFILE;
13308c2ecf20Sopenharmony_ci		if (stack)
13318c2ecf20Sopenharmony_ci			op = OP_STACK;
13328c2ecf20Sopenharmony_ci		else
13338c2ecf20Sopenharmony_ci			op = OP_CHANGE_PROFILE;
13348c2ecf20Sopenharmony_ci	}
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	if (*fqname == '&') {
13378c2ecf20Sopenharmony_ci		stack = true;
13388c2ecf20Sopenharmony_ci		/* don't have label_parse() do stacking */
13398c2ecf20Sopenharmony_ci		fqname++;
13408c2ecf20Sopenharmony_ci	}
13418c2ecf20Sopenharmony_ci	target = aa_label_parse(label, fqname, GFP_KERNEL, true, false);
13428c2ecf20Sopenharmony_ci	if (IS_ERR(target)) {
13438c2ecf20Sopenharmony_ci		struct aa_profile *tprofile;
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci		info = "label not found";
13468c2ecf20Sopenharmony_ci		error = PTR_ERR(target);
13478c2ecf20Sopenharmony_ci		target = NULL;
13488c2ecf20Sopenharmony_ci		/*
13498c2ecf20Sopenharmony_ci		 * TODO: fixme using labels_profile is not right - do profile
13508c2ecf20Sopenharmony_ci		 * per complain profile
13518c2ecf20Sopenharmony_ci		 */
13528c2ecf20Sopenharmony_ci		if ((flags & AA_CHANGE_TEST) ||
13538c2ecf20Sopenharmony_ci		    !COMPLAIN_MODE(labels_profile(label)))
13548c2ecf20Sopenharmony_ci			goto audit;
13558c2ecf20Sopenharmony_ci		/* released below */
13568c2ecf20Sopenharmony_ci		tprofile = aa_new_null_profile(labels_profile(label), false,
13578c2ecf20Sopenharmony_ci					       fqname, GFP_KERNEL);
13588c2ecf20Sopenharmony_ci		if (!tprofile) {
13598c2ecf20Sopenharmony_ci			info = "failed null profile create";
13608c2ecf20Sopenharmony_ci			error = -ENOMEM;
13618c2ecf20Sopenharmony_ci			goto audit;
13628c2ecf20Sopenharmony_ci		}
13638c2ecf20Sopenharmony_ci		target = &tprofile->label;
13648c2ecf20Sopenharmony_ci		goto check;
13658c2ecf20Sopenharmony_ci	}
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	/*
13688c2ecf20Sopenharmony_ci	 * self directed transitions only apply to current policy ns
13698c2ecf20Sopenharmony_ci	 * TODO: currently requiring perms for stacking and straight change
13708c2ecf20Sopenharmony_ci	 *       stacking doesn't strictly need this. Determine how much
13718c2ecf20Sopenharmony_ci	 *       we want to loosen this restriction for stacking
13728c2ecf20Sopenharmony_ci	 *
13738c2ecf20Sopenharmony_ci	 * if (!stack) {
13748c2ecf20Sopenharmony_ci	 */
13758c2ecf20Sopenharmony_ci	error = fn_for_each_in_ns(label, profile,
13768c2ecf20Sopenharmony_ci			change_profile_perms_wrapper(op, auditname,
13778c2ecf20Sopenharmony_ci						     profile, target, stack,
13788c2ecf20Sopenharmony_ci						     request, &perms));
13798c2ecf20Sopenharmony_ci	if (error)
13808c2ecf20Sopenharmony_ci		/* auditing done in change_profile_perms_wrapper */
13818c2ecf20Sopenharmony_ci		goto out;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	/* } */
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_cicheck:
13868c2ecf20Sopenharmony_ci	/* check if tracing task is allowed to trace target domain */
13878c2ecf20Sopenharmony_ci	error = may_change_ptraced_domain(target, &info);
13888c2ecf20Sopenharmony_ci	if (error && !fn_for_each_in_ns(label, profile,
13898c2ecf20Sopenharmony_ci					COMPLAIN_MODE(profile)))
13908c2ecf20Sopenharmony_ci		goto audit;
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	/* TODO: add permission check to allow this
13938c2ecf20Sopenharmony_ci	 * if ((flags & AA_CHANGE_ONEXEC) && !current_is_single_threaded()) {
13948c2ecf20Sopenharmony_ci	 *      info = "not a single threaded task";
13958c2ecf20Sopenharmony_ci	 *      error = -EACCES;
13968c2ecf20Sopenharmony_ci	 *      goto audit;
13978c2ecf20Sopenharmony_ci	 * }
13988c2ecf20Sopenharmony_ci	 */
13998c2ecf20Sopenharmony_ci	if (flags & AA_CHANGE_TEST)
14008c2ecf20Sopenharmony_ci		goto out;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	/* stacking is always a subset, so only check the nonstack case */
14038c2ecf20Sopenharmony_ci	if (!stack) {
14048c2ecf20Sopenharmony_ci		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
14058c2ecf20Sopenharmony_ci					   aa_get_label(target),
14068c2ecf20Sopenharmony_ci					   aa_get_label(&profile->label));
14078c2ecf20Sopenharmony_ci		/*
14088c2ecf20Sopenharmony_ci		 * no new privs prevents domain transitions that would
14098c2ecf20Sopenharmony_ci		 * reduce restrictions.
14108c2ecf20Sopenharmony_ci		 */
14118c2ecf20Sopenharmony_ci		if (task_no_new_privs(current) && !unconfined(label) &&
14128c2ecf20Sopenharmony_ci		    !aa_label_is_unconfined_subset(new, ctx->nnp)) {
14138c2ecf20Sopenharmony_ci			/* not an apparmor denial per se, so don't log it */
14148c2ecf20Sopenharmony_ci			AA_DEBUG("no_new_privs - change_hat denied");
14158c2ecf20Sopenharmony_ci			error = -EPERM;
14168c2ecf20Sopenharmony_ci			goto out;
14178c2ecf20Sopenharmony_ci		}
14188c2ecf20Sopenharmony_ci	}
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	if (!(flags & AA_CHANGE_ONEXEC)) {
14218c2ecf20Sopenharmony_ci		/* only transition profiles in the current ns */
14228c2ecf20Sopenharmony_ci		if (stack)
14238c2ecf20Sopenharmony_ci			new = aa_label_merge(label, target, GFP_KERNEL);
14248c2ecf20Sopenharmony_ci		if (IS_ERR_OR_NULL(new)) {
14258c2ecf20Sopenharmony_ci			info = "failed to build target label";
14268c2ecf20Sopenharmony_ci			if (!new)
14278c2ecf20Sopenharmony_ci				error = -ENOMEM;
14288c2ecf20Sopenharmony_ci			else
14298c2ecf20Sopenharmony_ci				error = PTR_ERR(new);
14308c2ecf20Sopenharmony_ci			new = NULL;
14318c2ecf20Sopenharmony_ci			perms.allow = 0;
14328c2ecf20Sopenharmony_ci			goto audit;
14338c2ecf20Sopenharmony_ci		}
14348c2ecf20Sopenharmony_ci		error = aa_replace_current_label(new);
14358c2ecf20Sopenharmony_ci	} else {
14368c2ecf20Sopenharmony_ci		if (new) {
14378c2ecf20Sopenharmony_ci			aa_put_label(new);
14388c2ecf20Sopenharmony_ci			new = NULL;
14398c2ecf20Sopenharmony_ci		}
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci		/* full transition will be built in exec path */
14428c2ecf20Sopenharmony_ci		error = aa_set_current_onexec(target, stack);
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ciaudit:
14468c2ecf20Sopenharmony_ci	error = fn_for_each_in_ns(label, profile,
14478c2ecf20Sopenharmony_ci			aa_audit_file(profile, &perms, op, request, auditname,
14488c2ecf20Sopenharmony_ci				      NULL, new ? new : target,
14498c2ecf20Sopenharmony_ci				      GLOBAL_ROOT_UID, info, error));
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ciout:
14528c2ecf20Sopenharmony_ci	aa_put_label(new);
14538c2ecf20Sopenharmony_ci	aa_put_label(target);
14548c2ecf20Sopenharmony_ci	aa_put_label(label);
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	return error;
14578c2ecf20Sopenharmony_ci}
1458