xref: /openbmc/linux/security/apparmor/domain.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2898127c3SJohn Johansen /*
3898127c3SJohn Johansen  * AppArmor security module
4898127c3SJohn Johansen  *
5898127c3SJohn Johansen  * This file contains AppArmor policy attachment and domain transitions
6898127c3SJohn Johansen  *
7898127c3SJohn Johansen  * Copyright (C) 2002-2008 Novell/SUSE
8898127c3SJohn Johansen  * Copyright 2009-2010 Canonical Ltd.
9898127c3SJohn Johansen  */
10898127c3SJohn Johansen 
11898127c3SJohn Johansen #include <linux/errno.h>
12898127c3SJohn Johansen #include <linux/fdtable.h>
133cee6079SChristian Brauner #include <linux/fs.h>
14898127c3SJohn Johansen #include <linux/file.h>
15898127c3SJohn Johansen #include <linux/mount.h>
16898127c3SJohn Johansen #include <linux/syscalls.h>
17898127c3SJohn Johansen #include <linux/personality.h>
188e51f908SMatthew Garrett #include <linux/xattr.h>
193cee6079SChristian Brauner #include <linux/user_namespace.h>
20898127c3SJohn Johansen 
21898127c3SJohn Johansen #include "include/audit.h"
22898127c3SJohn Johansen #include "include/apparmorfs.h"
23d8889d49SJohn Johansen #include "include/cred.h"
24898127c3SJohn Johansen #include "include/domain.h"
25898127c3SJohn Johansen #include "include/file.h"
26898127c3SJohn Johansen #include "include/ipc.h"
27898127c3SJohn Johansen #include "include/match.h"
28898127c3SJohn Johansen #include "include/path.h"
29898127c3SJohn Johansen #include "include/policy.h"
30cff281f6SJohn Johansen #include "include/policy_ns.h"
31898127c3SJohn Johansen 
32898127c3SJohn Johansen /**
33898127c3SJohn Johansen  * may_change_ptraced_domain - check if can change profile on ptraced task
34*690f33e1SJohn Johansen  * @cred: cred of task changing domain
35b2d09ae4SJohn Johansen  * @to_label: profile to change to  (NOT NULL)
36b2d09ae4SJohn Johansen  * @info: message if there is an error
37898127c3SJohn Johansen  *
3851775fe7SOleg Nesterov  * Check if current is ptraced and if so if the tracing task is allowed
39898127c3SJohn Johansen  * to trace the new domain
40898127c3SJohn Johansen  *
41898127c3SJohn Johansen  * Returns: %0 or error if change not allowed
42898127c3SJohn Johansen  */
may_change_ptraced_domain(const struct cred * to_cred,struct aa_label * to_label,const char ** info)43*690f33e1SJohn Johansen static int may_change_ptraced_domain(const struct cred *to_cred,
44*690f33e1SJohn Johansen 				     struct aa_label *to_label,
45b2d09ae4SJohn Johansen 				     const char **info)
46898127c3SJohn Johansen {
47898127c3SJohn Johansen 	struct task_struct *tracer;
48637f688dSJohn Johansen 	struct aa_label *tracerl = NULL;
49*690f33e1SJohn Johansen 	const struct cred *tracer_cred = NULL;
50*690f33e1SJohn Johansen 
51898127c3SJohn Johansen 	int error = 0;
52898127c3SJohn Johansen 
53898127c3SJohn Johansen 	rcu_read_lock();
5451775fe7SOleg Nesterov 	tracer = ptrace_parent(current);
55*690f33e1SJohn Johansen 	if (tracer) {
56898127c3SJohn Johansen 		/* released below */
57637f688dSJohn Johansen 		tracerl = aa_get_task_label(tracer);
58*690f33e1SJohn Johansen 		tracer_cred = get_task_cred(tracer);
59*690f33e1SJohn Johansen 	}
60898127c3SJohn Johansen 	/* not ptraced */
61637f688dSJohn Johansen 	if (!tracer || unconfined(tracerl))
62898127c3SJohn Johansen 		goto out;
63898127c3SJohn Johansen 
64*690f33e1SJohn Johansen 	error = aa_may_ptrace(tracer_cred, tracerl, to_cred, to_label,
65*690f33e1SJohn Johansen 			      PTRACE_MODE_ATTACH);
66898127c3SJohn Johansen 
67898127c3SJohn Johansen out:
6804fdc099SJohn Johansen 	rcu_read_unlock();
69637f688dSJohn Johansen 	aa_put_label(tracerl);
70*690f33e1SJohn Johansen 	put_cred(tracer_cred);
71898127c3SJohn Johansen 
72b2d09ae4SJohn Johansen 	if (error)
73b2d09ae4SJohn Johansen 		*info = "ptrace prevents transition";
74898127c3SJohn Johansen 	return error;
75898127c3SJohn Johansen }
76898127c3SJohn Johansen 
7793c98a48SJohn Johansen /**** TODO: dedup to aa_label_match - needs perm and dfa, merging
7893c98a48SJohn Johansen  * specifically this is an exact copy of aa_label_match except
7993c98a48SJohn Johansen  * aa_compute_perms is replaced with aa_compute_fperms
8093c98a48SJohn Johansen  * and policy.dfa with file.dfa
8193c98a48SJohn Johansen  ****/
8293c98a48SJohn Johansen /* match a profile and its associated ns component if needed
8393c98a48SJohn Johansen  * Assumes visibility test has already been done.
8493c98a48SJohn Johansen  * If a subns profile is not to be matched should be prescreened with
8593c98a48SJohn Johansen  * visibility test.
8693c98a48SJohn Johansen  */
match_component(struct aa_profile * profile,struct aa_profile * tp,bool stack,aa_state_t state)8733fc95d8SJohn Johansen static inline aa_state_t match_component(struct aa_profile *profile,
8893c98a48SJohn Johansen 					 struct aa_profile *tp,
8933fc95d8SJohn Johansen 					 bool stack, aa_state_t state)
9093c98a48SJohn Johansen {
911ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
921ad22fccSJohn Johansen 						    typeof(*rules), list);
9393c98a48SJohn Johansen 	const char *ns_name;
9493c98a48SJohn Johansen 
9593c98a48SJohn Johansen 	if (stack)
96217af7e2SJohn Johansen 		state = aa_dfa_match(rules->file.dfa, state, "&");
9793c98a48SJohn Johansen 	if (profile->ns == tp->ns)
98217af7e2SJohn Johansen 		return aa_dfa_match(rules->file.dfa, state, tp->base.hname);
9993c98a48SJohn Johansen 
10093c98a48SJohn Johansen 	/* try matching with namespace name and then profile */
10193c98a48SJohn Johansen 	ns_name = aa_ns_name(profile->ns, tp->ns, true);
102217af7e2SJohn Johansen 	state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
103217af7e2SJohn Johansen 	state = aa_dfa_match(rules->file.dfa, state, ns_name);
104217af7e2SJohn Johansen 	state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
105217af7e2SJohn Johansen 	return aa_dfa_match(rules->file.dfa, state, tp->base.hname);
10693c98a48SJohn Johansen }
10793c98a48SJohn Johansen 
10893c98a48SJohn Johansen /**
10993c98a48SJohn Johansen  * label_compound_match - find perms for full compound label
11093c98a48SJohn Johansen  * @profile: profile to find perms for
11193c98a48SJohn Johansen  * @label: label to check access permissions for
11293c98a48SJohn Johansen  * @stack: whether this is a stacking request
113bab1f77fSYang Li  * @state: state to start match in
11493c98a48SJohn Johansen  * @subns: whether to do permission checks on components in a subns
11593c98a48SJohn Johansen  * @request: permissions to request
11693c98a48SJohn Johansen  * @perms: perms struct to set
11793c98a48SJohn Johansen  *
11893c98a48SJohn Johansen  * Returns: 0 on success else ERROR
11993c98a48SJohn Johansen  *
12093c98a48SJohn Johansen  * For the label A//&B//&C this does the perm match for A//&B//&C
12193c98a48SJohn Johansen  * @perms should be preinitialized with allperms OR a previous permission
12293c98a48SJohn Johansen  *        check to be stacked.
12393c98a48SJohn Johansen  */
label_compound_match(struct aa_profile * profile,struct aa_label * label,bool stack,aa_state_t state,bool subns,u32 request,struct aa_perms * perms)12493c98a48SJohn Johansen static int label_compound_match(struct aa_profile *profile,
12593c98a48SJohn Johansen 				struct aa_label *label, bool stack,
12633fc95d8SJohn Johansen 				aa_state_t state, bool subns, u32 request,
12793c98a48SJohn Johansen 				struct aa_perms *perms)
12893c98a48SJohn Johansen {
1291ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
1301ad22fccSJohn Johansen 						    typeof(*rules), list);
13193c98a48SJohn Johansen 	struct aa_profile *tp;
13293c98a48SJohn Johansen 	struct label_it i;
13393c98a48SJohn Johansen 	struct path_cond cond = { };
13493c98a48SJohn Johansen 
13593c98a48SJohn Johansen 	/* find first subcomponent that is visible */
13693c98a48SJohn Johansen 	label_for_each(i, label, tp) {
13793c98a48SJohn Johansen 		if (!aa_ns_visible(profile->ns, tp->ns, subns))
13893c98a48SJohn Johansen 			continue;
13993c98a48SJohn Johansen 		state = match_component(profile, tp, stack, state);
14093c98a48SJohn Johansen 		if (!state)
14193c98a48SJohn Johansen 			goto fail;
14293c98a48SJohn Johansen 		goto next;
14393c98a48SJohn Johansen 	}
14493c98a48SJohn Johansen 
14593c98a48SJohn Johansen 	/* no component visible */
14693c98a48SJohn Johansen 	*perms = allperms;
14793c98a48SJohn Johansen 	return 0;
14893c98a48SJohn Johansen 
14993c98a48SJohn Johansen next:
15093c98a48SJohn Johansen 	label_for_each_cont(i, label, tp) {
15193c98a48SJohn Johansen 		if (!aa_ns_visible(profile->ns, tp->ns, subns))
15293c98a48SJohn Johansen 			continue;
153217af7e2SJohn Johansen 		state = aa_dfa_match(rules->file.dfa, state, "//&");
15493c98a48SJohn Johansen 		state = match_component(profile, tp, false, state);
15593c98a48SJohn Johansen 		if (!state)
15693c98a48SJohn Johansen 			goto fail;
15793c98a48SJohn Johansen 	}
158217af7e2SJohn Johansen 	*perms = *(aa_lookup_fperms(&(rules->file), state, &cond));
15993c98a48SJohn Johansen 	aa_apply_modes_to_perms(profile, perms);
16093c98a48SJohn Johansen 	if ((perms->allow & request) != request)
16193c98a48SJohn Johansen 		return -EACCES;
16293c98a48SJohn Johansen 
16393c98a48SJohn Johansen 	return 0;
16493c98a48SJohn Johansen 
16593c98a48SJohn Johansen fail:
16693c98a48SJohn Johansen 	*perms = nullperms;
16793c98a48SJohn Johansen 	return -EACCES;
16893c98a48SJohn Johansen }
16993c98a48SJohn Johansen 
17093c98a48SJohn Johansen /**
17193c98a48SJohn Johansen  * label_components_match - find perms for all subcomponents of a label
17293c98a48SJohn Johansen  * @profile: profile to find perms for
17393c98a48SJohn Johansen  * @label: label to check access permissions for
17493c98a48SJohn Johansen  * @stack: whether this is a stacking request
17593c98a48SJohn Johansen  * @start: state to start match in
17693c98a48SJohn Johansen  * @subns: whether to do permission checks on components in a subns
17793c98a48SJohn Johansen  * @request: permissions to request
17893c98a48SJohn Johansen  * @perms: an initialized perms struct to add accumulation to
17993c98a48SJohn Johansen  *
18093c98a48SJohn Johansen  * Returns: 0 on success else ERROR
18193c98a48SJohn Johansen  *
18293c98a48SJohn Johansen  * For the label A//&B//&C this does the perm match for each of A and B and C
18393c98a48SJohn Johansen  * @perms should be preinitialized with allperms OR a previous permission
18493c98a48SJohn Johansen  *        check to be stacked.
18593c98a48SJohn Johansen  */
label_components_match(struct aa_profile * profile,struct aa_label * label,bool stack,aa_state_t start,bool subns,u32 request,struct aa_perms * perms)18693c98a48SJohn Johansen static int label_components_match(struct aa_profile *profile,
18793c98a48SJohn Johansen 				  struct aa_label *label, bool stack,
18833fc95d8SJohn Johansen 				  aa_state_t start, bool subns, u32 request,
18993c98a48SJohn Johansen 				  struct aa_perms *perms)
19093c98a48SJohn Johansen {
1911ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
1921ad22fccSJohn Johansen 						    typeof(*rules), list);
19393c98a48SJohn Johansen 	struct aa_profile *tp;
19493c98a48SJohn Johansen 	struct label_it i;
19593c98a48SJohn Johansen 	struct aa_perms tmp;
19693c98a48SJohn Johansen 	struct path_cond cond = { };
19733fc95d8SJohn Johansen 	aa_state_t state = 0;
19893c98a48SJohn Johansen 
19993c98a48SJohn Johansen 	/* find first subcomponent to test */
20093c98a48SJohn Johansen 	label_for_each(i, label, tp) {
20193c98a48SJohn Johansen 		if (!aa_ns_visible(profile->ns, tp->ns, subns))
20293c98a48SJohn Johansen 			continue;
20393c98a48SJohn Johansen 		state = match_component(profile, tp, stack, start);
20493c98a48SJohn Johansen 		if (!state)
20593c98a48SJohn Johansen 			goto fail;
20693c98a48SJohn Johansen 		goto next;
20793c98a48SJohn Johansen 	}
20893c98a48SJohn Johansen 
20993c98a48SJohn Johansen 	/* no subcomponents visible - no change in perms */
21093c98a48SJohn Johansen 	return 0;
21193c98a48SJohn Johansen 
21293c98a48SJohn Johansen next:
213217af7e2SJohn Johansen 	tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
21493c98a48SJohn Johansen 	aa_apply_modes_to_perms(profile, &tmp);
21593c98a48SJohn Johansen 	aa_perms_accum(perms, &tmp);
21693c98a48SJohn Johansen 	label_for_each_cont(i, label, tp) {
21793c98a48SJohn Johansen 		if (!aa_ns_visible(profile->ns, tp->ns, subns))
21893c98a48SJohn Johansen 			continue;
21993c98a48SJohn Johansen 		state = match_component(profile, tp, stack, start);
22093c98a48SJohn Johansen 		if (!state)
22193c98a48SJohn Johansen 			goto fail;
222217af7e2SJohn Johansen 		tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
22393c98a48SJohn Johansen 		aa_apply_modes_to_perms(profile, &tmp);
22493c98a48SJohn Johansen 		aa_perms_accum(perms, &tmp);
22593c98a48SJohn Johansen 	}
22693c98a48SJohn Johansen 
22793c98a48SJohn Johansen 	if ((perms->allow & request) != request)
22893c98a48SJohn Johansen 		return -EACCES;
22993c98a48SJohn Johansen 
23093c98a48SJohn Johansen 	return 0;
23193c98a48SJohn Johansen 
23293c98a48SJohn Johansen fail:
23393c98a48SJohn Johansen 	*perms = nullperms;
23493c98a48SJohn Johansen 	return -EACCES;
23593c98a48SJohn Johansen }
23693c98a48SJohn Johansen 
23793c98a48SJohn Johansen /**
23893c98a48SJohn Johansen  * label_match - do a multi-component label match
23993c98a48SJohn Johansen  * @profile: profile to match against (NOT NULL)
24093c98a48SJohn Johansen  * @label: label to match (NOT NULL)
24193c98a48SJohn Johansen  * @stack: whether this is a stacking request
24293c98a48SJohn Johansen  * @state: state to start in
24393c98a48SJohn Johansen  * @subns: whether to match subns components
24493c98a48SJohn Johansen  * @request: permission request
24593c98a48SJohn Johansen  * @perms: Returns computed perms (NOT NULL)
24693c98a48SJohn Johansen  *
24793c98a48SJohn Johansen  * Returns: the state the match finished in, may be the none matching state
24893c98a48SJohn Johansen  */
label_match(struct aa_profile * profile,struct aa_label * label,bool stack,aa_state_t state,bool subns,u32 request,struct aa_perms * perms)24993c98a48SJohn Johansen static int label_match(struct aa_profile *profile, struct aa_label *label,
25033fc95d8SJohn Johansen 		       bool stack, aa_state_t state, bool subns, u32 request,
25193c98a48SJohn Johansen 		       struct aa_perms *perms)
25293c98a48SJohn Johansen {
25393c98a48SJohn Johansen 	int error;
25493c98a48SJohn Johansen 
25593c98a48SJohn Johansen 	*perms = nullperms;
25693c98a48SJohn Johansen 	error = label_compound_match(profile, label, stack, state, subns,
25793c98a48SJohn Johansen 				     request, perms);
25893c98a48SJohn Johansen 	if (!error)
25993c98a48SJohn Johansen 		return error;
26093c98a48SJohn Johansen 
26193c98a48SJohn Johansen 	*perms = allperms;
26293c98a48SJohn Johansen 	return label_components_match(profile, label, stack, state, subns,
26393c98a48SJohn Johansen 				      request, perms);
26493c98a48SJohn Johansen }
26593c98a48SJohn Johansen 
26693c98a48SJohn Johansen /******* end TODO: dedup *****/
26793c98a48SJohn Johansen 
268898127c3SJohn Johansen /**
269898127c3SJohn Johansen  * change_profile_perms - find permissions for change_profile
270898127c3SJohn Johansen  * @profile: the current profile  (NOT NULL)
27193c98a48SJohn Johansen  * @target: label to transition to (NOT NULL)
27293c98a48SJohn Johansen  * @stack: whether this is a stacking request
273898127c3SJohn Johansen  * @request: requested perms
274898127c3SJohn Johansen  * @start: state to start matching in
275898127c3SJohn Johansen  *
27693c98a48SJohn Johansen  *
277898127c3SJohn Johansen  * Returns: permission set
27893c98a48SJohn Johansen  *
27993c98a48SJohn Johansen  * currently only matches full label A//&B//&C or individual components A, B, C
28093c98a48SJohn Johansen  * not arbitrary combinations. Eg. A//&B, C
281898127c3SJohn Johansen  */
change_profile_perms(struct aa_profile * profile,struct aa_label * target,bool stack,u32 request,aa_state_t start,struct aa_perms * perms)28293c98a48SJohn Johansen static int change_profile_perms(struct aa_profile *profile,
28393c98a48SJohn Johansen 				struct aa_label *target, bool stack,
28433fc95d8SJohn Johansen 				u32 request, aa_state_t start,
28593c98a48SJohn Johansen 				struct aa_perms *perms)
28693c98a48SJohn Johansen {
28793c98a48SJohn Johansen 	if (profile_unconfined(profile)) {
28893c98a48SJohn Johansen 		perms->allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC;
28993c98a48SJohn Johansen 		perms->audit = perms->quiet = perms->kill = 0;
29093c98a48SJohn Johansen 		return 0;
29193c98a48SJohn Johansen 	}
29293c98a48SJohn Johansen 
29393c98a48SJohn Johansen 	/* TODO: add profile in ns screening */
29493c98a48SJohn Johansen 	return label_match(profile, target, stack, start, true, request, perms);
29593c98a48SJohn Johansen }
29693c98a48SJohn Johansen 
297898127c3SJohn Johansen /**
2988e51f908SMatthew Garrett  * aa_xattrs_match - check whether a file matches the xattrs defined in profile
2998e51f908SMatthew Garrett  * @bprm: binprm struct for the process to validate
3008e51f908SMatthew Garrett  * @profile: profile to match against (NOT NULL)
30173f488cdSJohn Johansen  * @state: state to start match in
3028e51f908SMatthew Garrett  *
3038e51f908SMatthew Garrett  * Returns: number of extended attributes that matched, or < 0 on error
3048e51f908SMatthew Garrett  */
aa_xattrs_match(const struct linux_binprm * bprm,struct aa_profile * profile,aa_state_t state)3058e51f908SMatthew Garrett static int aa_xattrs_match(const struct linux_binprm *bprm,
30633fc95d8SJohn Johansen 			   struct aa_profile *profile, aa_state_t state)
3078e51f908SMatthew Garrett {
3088e51f908SMatthew Garrett 	int i;
3098e51f908SMatthew Garrett 	struct dentry *d;
3108e51f908SMatthew Garrett 	char *value = NULL;
311217af7e2SJohn Johansen 	struct aa_attachment *attach = &profile->attach;
31293761c93SLinus Torvalds 	int size, value_size = 0, ret = attach->xattr_count;
3138e51f908SMatthew Garrett 
314217af7e2SJohn Johansen 	if (!bprm || !attach->xattr_count)
3158e51f908SMatthew Garrett 		return 0;
3168c62ed27SJohn Johansen 	might_sleep();
3178e51f908SMatthew Garrett 
31873f488cdSJohn Johansen 	/* transition from exec match to xattr set */
319217af7e2SJohn Johansen 	state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
3208e51f908SMatthew Garrett 	d = bprm->file->f_path.dentry;
3218e51f908SMatthew Garrett 
322217af7e2SJohn Johansen 	for (i = 0; i < attach->xattr_count; i++) {
3234609e1f1SChristian Brauner 		size = vfs_getxattr_alloc(&nop_mnt_idmap, d, attach->xattrs[i],
324c7c7a1a1STycho Andersen 					  &value, value_size, GFP_KERNEL);
32573f488cdSJohn Johansen 		if (size >= 0) {
3262d63dd43SJohn Johansen 			u32 index, perm;
3278e51f908SMatthew Garrett 
3280df34a64SJohn Johansen 			/*
3290df34a64SJohn Johansen 			 * Check the xattr presence before value. This ensure
3300df34a64SJohn Johansen 			 * that not present xattr can be distinguished from a 0
3310df34a64SJohn Johansen 			 * length value or rule that matches any value
3320df34a64SJohn Johansen 			 */
333217af7e2SJohn Johansen 			state = aa_dfa_null_transition(attach->xmatch.dfa,
334048d4954SJohn Johansen 						       state);
3350df34a64SJohn Johansen 			/* Check xattr value */
336217af7e2SJohn Johansen 			state = aa_dfa_match_len(attach->xmatch.dfa, state,
337048d4954SJohn Johansen 						 value, size);
338217af7e2SJohn Johansen 			index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
339217af7e2SJohn Johansen 			perm = attach->xmatch.perms[index].allow;
34073f488cdSJohn Johansen 			if (!(perm & MAY_EXEC)) {
3418e51f908SMatthew Garrett 				ret = -EINVAL;
3428e51f908SMatthew Garrett 				goto out;
3438e51f908SMatthew Garrett 			}
34473f488cdSJohn Johansen 		}
34573f488cdSJohn Johansen 		/* transition to next element */
346217af7e2SJohn Johansen 		state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
34773f488cdSJohn Johansen 		if (size < 0) {
34873f488cdSJohn Johansen 			/*
34973f488cdSJohn Johansen 			 * No xattr match, so verify if transition to
35073f488cdSJohn Johansen 			 * next element was valid. IFF so the xattr
35173f488cdSJohn Johansen 			 * was optional.
35273f488cdSJohn Johansen 			 */
35373f488cdSJohn Johansen 			if (!state) {
3548e51f908SMatthew Garrett 				ret = -EINVAL;
3558e51f908SMatthew Garrett 				goto out;
3568e51f908SMatthew Garrett 			}
35773f488cdSJohn Johansen 			/* don't count missing optional xattr as matched */
35873f488cdSJohn Johansen 			ret--;
3598e51f908SMatthew Garrett 		}
3608e51f908SMatthew Garrett 	}
3618e51f908SMatthew Garrett 
3628e51f908SMatthew Garrett out:
3638e51f908SMatthew Garrett 	kfree(value);
3648e51f908SMatthew Garrett 	return ret;
3658e51f908SMatthew Garrett }
3668e51f908SMatthew Garrett 
3678e51f908SMatthew Garrett /**
3688c62ed27SJohn Johansen  * find_attach - do attachment search for unconfined processes
3698e51f908SMatthew Garrett  * @bprm - binprm structure of transitioning task
3708c62ed27SJohn Johansen  * @ns: the current namespace  (NOT NULL)
371898127c3SJohn Johansen  * @head - profile list to walk  (NOT NULL)
3728c62ed27SJohn Johansen  * @name - to match against  (NOT NULL)
373844b8292SJohn Johansen  * @info - info message if there was an error (NOT NULL)
374898127c3SJohn Johansen  *
375898127c3SJohn Johansen  * Do a linear search on the profiles in the list.  There is a matching
376898127c3SJohn Johansen  * preference where an exact match is preferred over a name which uses
377898127c3SJohn Johansen  * expressions to match, and matching expressions with the greatest
378898127c3SJohn Johansen  * xmatch_len are preferred.
379898127c3SJohn Johansen  *
380898127c3SJohn Johansen  * Requires: @head not be shared or have appropriate locks held
381898127c3SJohn Johansen  *
3828c62ed27SJohn Johansen  * Returns: label or NULL if no match found
383898127c3SJohn Johansen  */
find_attach(const struct linux_binprm * bprm,struct aa_ns * ns,struct list_head * head,const char * name,const char ** info)3848c62ed27SJohn Johansen static struct aa_label *find_attach(const struct linux_binprm *bprm,
3858c62ed27SJohn Johansen 				    struct aa_ns *ns, struct list_head *head,
3868c62ed27SJohn Johansen 				    const char *name, const char **info)
387898127c3SJohn Johansen {
38821f60661SJohn Johansen 	int candidate_len = 0, candidate_xattrs = 0;
389844b8292SJohn Johansen 	bool conflict = false;
390898127c3SJohn Johansen 	struct aa_profile *profile, *candidate = NULL;
391898127c3SJohn Johansen 
39221f60661SJohn Johansen 	AA_BUG(!name);
39321f60661SJohn Johansen 	AA_BUG(!head);
39421f60661SJohn Johansen 
3958c62ed27SJohn Johansen 	rcu_read_lock();
3968c62ed27SJohn Johansen restart:
39701e2b670SJohn Johansen 	list_for_each_entry_rcu(profile, head, base.list) {
398217af7e2SJohn Johansen 		struct aa_attachment *attach = &profile->attach;
399217af7e2SJohn Johansen 
40006d426d1SJohn Johansen 		if (profile->label.flags & FLAG_NULL &&
40106d426d1SJohn Johansen 		    &profile->label == ns_unconfined(profile->ns))
402898127c3SJohn Johansen 			continue;
40306d426d1SJohn Johansen 
4048e51f908SMatthew Garrett 		/* Find the "best" matching profile. Profiles must
4058e51f908SMatthew Garrett 		 * match the path and extended attributes (if any)
4068e51f908SMatthew Garrett 		 * associated with the file. A more specific path
4078e51f908SMatthew Garrett 		 * match will be preferred over a less specific one,
4088e51f908SMatthew Garrett 		 * and a match with more matching extended attributes
4098e51f908SMatthew Garrett 		 * will be preferred over one with fewer. If the best
4108e51f908SMatthew Garrett 		 * match has both the same level of path specificity
4118e51f908SMatthew Garrett 		 * and the same number of matching extended attributes
4128e51f908SMatthew Garrett 		 * as another profile, signal a conflict and refuse to
4138e51f908SMatthew Garrett 		 * match.
4148e51f908SMatthew Garrett 		 */
415217af7e2SJohn Johansen 		if (attach->xmatch.dfa) {
41633fc95d8SJohn Johansen 			unsigned int count;
41733fc95d8SJohn Johansen 			aa_state_t state;
4182d63dd43SJohn Johansen 			u32 index, perm;
419844b8292SJohn Johansen 
420217af7e2SJohn Johansen 			state = aa_dfa_leftmatch(attach->xmatch.dfa,
421217af7e2SJohn Johansen 					attach->xmatch.start[AA_CLASS_XMATCH],
42221f60661SJohn Johansen 					name, &count);
423217af7e2SJohn Johansen 			index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
424217af7e2SJohn Johansen 			perm = attach->xmatch.perms[index].allow;
425898127c3SJohn Johansen 			/* any accepting state means a valid match. */
426898127c3SJohn Johansen 			if (perm & MAY_EXEC) {
4278c62ed27SJohn Johansen 				int ret = 0;
4288e51f908SMatthew Garrett 
42921f60661SJohn Johansen 				if (count < candidate_len)
43021f60661SJohn Johansen 					continue;
43121f60661SJohn Johansen 
432217af7e2SJohn Johansen 				if (bprm && attach->xattr_count) {
4338c62ed27SJohn Johansen 					long rev = READ_ONCE(ns->revision);
4348c62ed27SJohn Johansen 
4358c62ed27SJohn Johansen 					if (!aa_get_profile_not0(profile))
4368c62ed27SJohn Johansen 						goto restart;
4378c62ed27SJohn Johansen 					rcu_read_unlock();
4388c62ed27SJohn Johansen 					ret = aa_xattrs_match(bprm, profile,
4398c62ed27SJohn Johansen 							      state);
4408c62ed27SJohn Johansen 					rcu_read_lock();
4418c62ed27SJohn Johansen 					aa_put_profile(profile);
4428c62ed27SJohn Johansen 					if (rev !=
4438c62ed27SJohn Johansen 					    READ_ONCE(ns->revision))
4448c62ed27SJohn Johansen 						/* policy changed */
4458c62ed27SJohn Johansen 						goto restart;
4468c62ed27SJohn Johansen 					/*
4478c62ed27SJohn Johansen 					 * Fail matching if the xattrs don't
4488c62ed27SJohn Johansen 					 * match
4498c62ed27SJohn Johansen 					 */
4508e51f908SMatthew Garrett 					if (ret < 0)
4518e51f908SMatthew Garrett 						continue;
4528c62ed27SJohn Johansen 				}
45373f488cdSJohn Johansen 				/*
45473f488cdSJohn Johansen 				 * TODO: allow for more flexible best match
45573f488cdSJohn Johansen 				 *
45673f488cdSJohn Johansen 				 * The new match isn't more specific
4578e51f908SMatthew Garrett 				 * than the current best match
4588e51f908SMatthew Garrett 				 */
45921f60661SJohn Johansen 				if (count == candidate_len &&
46021f60661SJohn Johansen 				    ret <= candidate_xattrs) {
4618e51f908SMatthew Garrett 					/* Match is equivalent, so conflict */
46221f60661SJohn Johansen 					if (ret == candidate_xattrs)
4631a3881d3SMatthew Garrett 						conflict = true;
4641a3881d3SMatthew Garrett 					continue;
4651a3881d3SMatthew Garrett 				}
4668e51f908SMatthew Garrett 
4678e51f908SMatthew Garrett 				/* Either the same length with more matching
4688e51f908SMatthew Garrett 				 * xattrs, or a longer match
4698e51f908SMatthew Garrett 				 */
470898127c3SJohn Johansen 				candidate = profile;
471217af7e2SJohn Johansen 				candidate_len = max(count, attach->xmatch_len);
47221f60661SJohn Johansen 				candidate_xattrs = ret;
473844b8292SJohn Johansen 				conflict = false;
474844b8292SJohn Johansen 			}
4758c62ed27SJohn Johansen 		} else if (!strcmp(profile->base.name, name)) {
47673f488cdSJohn Johansen 			/*
47773f488cdSJohn Johansen 			 * old exact non-re match, without conditionals such
47873f488cdSJohn Johansen 			 * as xattrs. no more searching required
47973f488cdSJohn Johansen 			 */
4808c62ed27SJohn Johansen 			candidate = profile;
4818c62ed27SJohn Johansen 			goto out;
4828c62ed27SJohn Johansen 		}
483898127c3SJohn Johansen 	}
484898127c3SJohn Johansen 
4858c62ed27SJohn Johansen 	if (!candidate || conflict) {
4868c62ed27SJohn Johansen 		if (conflict)
487844b8292SJohn Johansen 			*info = "conflicting profile attachments";
4888c62ed27SJohn Johansen 		rcu_read_unlock();
489844b8292SJohn Johansen 		return NULL;
490844b8292SJohn Johansen 	}
491844b8292SJohn Johansen 
4928c62ed27SJohn Johansen out:
4938c62ed27SJohn Johansen 	candidate = aa_get_newest_profile(candidate);
49401e2b670SJohn Johansen 	rcu_read_unlock();
495898127c3SJohn Johansen 
4968c62ed27SJohn Johansen 	return &candidate->label;
497898127c3SJohn Johansen }
498898127c3SJohn Johansen 
next_name(int xtype,const char * name)499898127c3SJohn Johansen static const char *next_name(int xtype, const char *name)
500898127c3SJohn Johansen {
501898127c3SJohn Johansen 	return NULL;
502898127c3SJohn Johansen }
503898127c3SJohn Johansen 
504898127c3SJohn Johansen /**
505898127c3SJohn Johansen  * x_table_lookup - lookup an x transition name via transition table
506898127c3SJohn Johansen  * @profile: current profile (NOT NULL)
507898127c3SJohn Johansen  * @xindex: index into x transition table
50893c98a48SJohn Johansen  * @name: returns: name tested to find label (NOT NULL)
509898127c3SJohn Johansen  *
51093c98a48SJohn Johansen  * Returns: refcounted label, or NULL on failure (MAYBE NULL)
511898127c3SJohn Johansen  */
x_table_lookup(struct aa_profile * profile,u32 xindex,const char ** name)5122ea3ffb7SJohn Johansen struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
51393c98a48SJohn Johansen 				const char **name)
514898127c3SJohn Johansen {
5151ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
5161ad22fccSJohn Johansen 						    typeof(*rules), list);
51793c98a48SJohn Johansen 	struct aa_label *label = NULL;
518898127c3SJohn Johansen 	u32 xtype = xindex & AA_X_TYPE_MASK;
519898127c3SJohn Johansen 	int index = xindex & AA_X_INDEX_MASK;
52093c98a48SJohn Johansen 
52193c98a48SJohn Johansen 	AA_BUG(!name);
522898127c3SJohn Johansen 
523898127c3SJohn Johansen 	/* index is guaranteed to be in range, validated at load time */
52493c98a48SJohn Johansen 	/* TODO: move lookup parsing to unpack time so this is a straight
52593c98a48SJohn Johansen 	 *       index into the resultant label
52693c98a48SJohn Johansen 	 */
527217af7e2SJohn Johansen 	for (*name = rules->file.trans.table[index]; !label && *name;
52893c98a48SJohn Johansen 	     *name = next_name(xtype, *name)) {
529898127c3SJohn Johansen 		if (xindex & AA_X_CHILD) {
53093c98a48SJohn Johansen 			struct aa_profile *new_profile;
531898127c3SJohn Johansen 			/* release by caller */
53293c98a48SJohn Johansen 			new_profile = aa_find_child(profile, *name);
53393c98a48SJohn Johansen 			if (new_profile)
53493c98a48SJohn Johansen 				label = &new_profile->label;
535898127c3SJohn Johansen 			continue;
536898127c3SJohn Johansen 		}
5378ac2ca32SSebastian Andrzej Siewior 		label = aa_label_parse(&profile->label, *name, GFP_KERNEL,
53893c98a48SJohn Johansen 				       true, false);
53993c98a48SJohn Johansen 		if (IS_ERR(label))
54093c98a48SJohn Johansen 			label = NULL;
541898127c3SJohn Johansen 	}
542898127c3SJohn Johansen 
543898127c3SJohn Johansen 	/* released by caller */
544898127c3SJohn Johansen 
54593c98a48SJohn Johansen 	return label;
546898127c3SJohn Johansen }
547898127c3SJohn Johansen 
548898127c3SJohn Johansen /**
54993c98a48SJohn Johansen  * x_to_label - get target label for a given xindex
550898127c3SJohn Johansen  * @profile: current profile  (NOT NULL)
5518e51f908SMatthew Garrett  * @bprm: binprm structure of transitioning task
552898127c3SJohn Johansen  * @name: name to lookup (NOT NULL)
553898127c3SJohn Johansen  * @xindex: index into x transition table
55493c98a48SJohn Johansen  * @lookupname: returns: name used in lookup if one was specified (NOT NULL)
555898127c3SJohn Johansen  *
55693c98a48SJohn Johansen  * find label for a transition index
557898127c3SJohn Johansen  *
55893c98a48SJohn Johansen  * Returns: refcounted label or NULL if not found available
559898127c3SJohn Johansen  */
x_to_label(struct aa_profile * profile,const struct linux_binprm * bprm,const char * name,u32 xindex,const char ** lookupname,const char ** info)56093c98a48SJohn Johansen static struct aa_label *x_to_label(struct aa_profile *profile,
5618e51f908SMatthew Garrett 				   const struct linux_binprm *bprm,
56293c98a48SJohn Johansen 				   const char *name, u32 xindex,
56393c98a48SJohn Johansen 				   const char **lookupname,
56493c98a48SJohn Johansen 				   const char **info)
565898127c3SJohn Johansen {
5661ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
5671ad22fccSJohn Johansen 						    typeof(*rules), list);
56893c98a48SJohn Johansen 	struct aa_label *new = NULL;
56998849dffSJohn Johansen 	struct aa_ns *ns = profile->ns;
570898127c3SJohn Johansen 	u32 xtype = xindex & AA_X_TYPE_MASK;
57193c98a48SJohn Johansen 	const char *stack = NULL;
572898127c3SJohn Johansen 
573898127c3SJohn Johansen 	switch (xtype) {
574898127c3SJohn Johansen 	case AA_X_NONE:
575898127c3SJohn Johansen 		/* fail exec unless ix || ux fallback - handled by caller */
57693c98a48SJohn Johansen 		*lookupname = NULL;
57793c98a48SJohn Johansen 		break;
57893c98a48SJohn Johansen 	case AA_X_TABLE:
57993c98a48SJohn Johansen 		/* TODO: fix when perm mapping done at unload */
580217af7e2SJohn Johansen 		stack = rules->file.trans.table[xindex & AA_X_INDEX_MASK];
58193c98a48SJohn Johansen 		if (*stack != '&') {
58293c98a48SJohn Johansen 			/* released by caller */
58393c98a48SJohn Johansen 			new = x_table_lookup(profile, xindex, lookupname);
58493c98a48SJohn Johansen 			stack = NULL;
58593c98a48SJohn Johansen 			break;
58693c98a48SJohn Johansen 		}
587df561f66SGustavo A. R. Silva 		fallthrough;	/* to X_NAME */
588898127c3SJohn Johansen 	case AA_X_NAME:
589898127c3SJohn Johansen 		if (xindex & AA_X_CHILD)
590898127c3SJohn Johansen 			/* released by caller */
5918e51f908SMatthew Garrett 			new = find_attach(bprm, ns, &profile->base.profiles,
592844b8292SJohn Johansen 					  name, info);
593898127c3SJohn Johansen 		else
594898127c3SJohn Johansen 			/* released by caller */
5958e51f908SMatthew Garrett 			new = find_attach(bprm, ns, &ns->base.profiles,
596844b8292SJohn Johansen 					  name, info);
59793c98a48SJohn Johansen 		*lookupname = name;
598898127c3SJohn Johansen 		break;
599898127c3SJohn Johansen 	}
600898127c3SJohn Johansen 
60193c98a48SJohn Johansen 	if (!new) {
60293c98a48SJohn Johansen 		if (xindex & AA_X_INHERIT) {
60393c98a48SJohn Johansen 			/* (p|c|n)ix - don't change profile but do
60493c98a48SJohn Johansen 			 * use the newest version
60593c98a48SJohn Johansen 			 */
60693c98a48SJohn Johansen 			*info = "ix fallback";
60793c98a48SJohn Johansen 			/* no profile && no error */
60893c98a48SJohn Johansen 			new = aa_get_newest_label(&profile->label);
60993c98a48SJohn Johansen 		} else if (xindex & AA_X_UNCONFINED) {
61093c98a48SJohn Johansen 			new = aa_get_newest_label(ns_unconfined(profile->ns));
61193c98a48SJohn Johansen 			*info = "ux fallback";
61293c98a48SJohn Johansen 		}
61393c98a48SJohn Johansen 	}
61493c98a48SJohn Johansen 
61593c98a48SJohn Johansen 	if (new && stack) {
61693c98a48SJohn Johansen 		/* base the stack on post domain transition */
61793c98a48SJohn Johansen 		struct aa_label *base = new;
61893c98a48SJohn Johansen 
6198ac2ca32SSebastian Andrzej Siewior 		new = aa_label_parse(base, stack, GFP_KERNEL, true, false);
62093c98a48SJohn Johansen 		if (IS_ERR(new))
62193c98a48SJohn Johansen 			new = NULL;
62293c98a48SJohn Johansen 		aa_put_label(base);
62393c98a48SJohn Johansen 	}
62493c98a48SJohn Johansen 
625898127c3SJohn Johansen 	/* released by caller */
62693c98a48SJohn Johansen 	return new;
62793c98a48SJohn Johansen }
62893c98a48SJohn Johansen 
profile_transition(const struct cred * subj_cred,struct aa_profile * profile,const struct linux_binprm * bprm,char * buffer,struct path_cond * cond,bool * secure_exec)629*690f33e1SJohn Johansen static struct aa_label *profile_transition(const struct cred *subj_cred,
630*690f33e1SJohn Johansen 					   struct aa_profile *profile,
63193c98a48SJohn Johansen 					   const struct linux_binprm *bprm,
63293c98a48SJohn Johansen 					   char *buffer, struct path_cond *cond,
63393c98a48SJohn Johansen 					   bool *secure_exec)
63493c98a48SJohn Johansen {
6351ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
6361ad22fccSJohn Johansen 						    typeof(*rules), list);
63793c98a48SJohn Johansen 	struct aa_label *new = NULL;
63893c98a48SJohn Johansen 	const char *info = NULL, *name = NULL, *target = NULL;
639217af7e2SJohn Johansen 	aa_state_t state = rules->file.start[AA_CLASS_FILE];
64093c98a48SJohn Johansen 	struct aa_perms perms = {};
64193c98a48SJohn Johansen 	bool nonewprivs = false;
64293c98a48SJohn Johansen 	int error = 0;
64393c98a48SJohn Johansen 
64493c98a48SJohn Johansen 	AA_BUG(!profile);
64593c98a48SJohn Johansen 	AA_BUG(!bprm);
64693c98a48SJohn Johansen 	AA_BUG(!buffer);
64793c98a48SJohn Johansen 
64893c98a48SJohn Johansen 	error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
64993c98a48SJohn Johansen 			     &name, &info, profile->disconnected);
65093c98a48SJohn Johansen 	if (error) {
65193c98a48SJohn Johansen 		if (profile_unconfined(profile) ||
65293c98a48SJohn Johansen 		    (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
65393c98a48SJohn Johansen 			AA_DEBUG("name lookup ix on error");
65493c98a48SJohn Johansen 			error = 0;
65593c98a48SJohn Johansen 			new = aa_get_newest_label(&profile->label);
65693c98a48SJohn Johansen 		}
65793c98a48SJohn Johansen 		name = bprm->filename;
65893c98a48SJohn Johansen 		goto audit;
65993c98a48SJohn Johansen 	}
66093c98a48SJohn Johansen 
66193c98a48SJohn Johansen 	if (profile_unconfined(profile)) {
6628e51f908SMatthew Garrett 		new = find_attach(bprm, profile->ns,
6638e51f908SMatthew Garrett 				  &profile->ns->base.profiles, name, &info);
66493c98a48SJohn Johansen 		if (new) {
66593c98a48SJohn Johansen 			AA_DEBUG("unconfined attached to new label");
66693c98a48SJohn Johansen 			return new;
66793c98a48SJohn Johansen 		}
66893c98a48SJohn Johansen 		AA_DEBUG("unconfined exec no attachment");
66993c98a48SJohn Johansen 		return aa_get_newest_label(&profile->label);
67093c98a48SJohn Johansen 	}
67193c98a48SJohn Johansen 
67293c98a48SJohn Johansen 	/* find exec permissions for name */
673217af7e2SJohn Johansen 	state = aa_str_perms(&(rules->file), state, name, cond, &perms);
67493c98a48SJohn Johansen 	if (perms.allow & MAY_EXEC) {
67593c98a48SJohn Johansen 		/* exec permission determine how to transition */
6768e51f908SMatthew Garrett 		new = x_to_label(profile, bprm, name, perms.xindex, &target,
6778e51f908SMatthew Garrett 				 &info);
67893c98a48SJohn Johansen 		if (new && new->proxy == profile->label.proxy && info) {
67993c98a48SJohn Johansen 			/* hack ix fallback - improve how this is detected */
68093c98a48SJohn Johansen 			goto audit;
68193c98a48SJohn Johansen 		} else if (!new) {
68293c98a48SJohn Johansen 			error = -EACCES;
68393c98a48SJohn Johansen 			info = "profile transition not found";
68493c98a48SJohn Johansen 			/* remove MAY_EXEC to audit as failure */
68593c98a48SJohn Johansen 			perms.allow &= ~MAY_EXEC;
68693c98a48SJohn Johansen 		}
68793c98a48SJohn Johansen 	} else if (COMPLAIN_MODE(profile)) {
68893c98a48SJohn Johansen 		/* no exec permission - learning mode */
6895d7c44efSJohn Johansen 		struct aa_profile *new_profile = NULL;
6905d7c44efSJohn Johansen 
69158f89ce5SJohn Johansen 		new_profile = aa_new_learning_profile(profile, false, name,
6925d7c44efSJohn Johansen 						      GFP_KERNEL);
69393c98a48SJohn Johansen 		if (!new_profile) {
69493c98a48SJohn Johansen 			error = -ENOMEM;
69593c98a48SJohn Johansen 			info = "could not create null profile";
69693c98a48SJohn Johansen 		} else {
69793c98a48SJohn Johansen 			error = -EACCES;
69893c98a48SJohn Johansen 			new = &new_profile->label;
69993c98a48SJohn Johansen 		}
70093c98a48SJohn Johansen 		perms.xindex |= AA_X_UNSAFE;
70193c98a48SJohn Johansen 	} else
70293c98a48SJohn Johansen 		/* fail exec */
70393c98a48SJohn Johansen 		error = -EACCES;
70493c98a48SJohn Johansen 
70593c98a48SJohn Johansen 	if (!new)
70693c98a48SJohn Johansen 		goto audit;
70793c98a48SJohn Johansen 
70893c98a48SJohn Johansen 
70993c98a48SJohn Johansen 	if (!(perms.xindex & AA_X_UNSAFE)) {
71093c98a48SJohn Johansen 		if (DEBUG_ON) {
71193c98a48SJohn Johansen 			dbg_printk("apparmor: scrubbing environment variables"
71293c98a48SJohn Johansen 				   " for %s profile=", name);
7138ac2ca32SSebastian Andrzej Siewior 			aa_label_printk(new, GFP_KERNEL);
71493c98a48SJohn Johansen 			dbg_printk("\n");
71593c98a48SJohn Johansen 		}
71693c98a48SJohn Johansen 		*secure_exec = true;
71793c98a48SJohn Johansen 	}
71893c98a48SJohn Johansen 
71993c98a48SJohn Johansen audit:
720*690f33e1SJohn Johansen 	aa_audit_file(subj_cred, profile, &perms, OP_EXEC, MAY_EXEC, name,
721*690f33e1SJohn Johansen 		      target, new,
72293c98a48SJohn Johansen 		      cond->uid, info, error);
72393c98a48SJohn Johansen 	if (!new || nonewprivs) {
72493c98a48SJohn Johansen 		aa_put_label(new);
72593c98a48SJohn Johansen 		return ERR_PTR(error);
72693c98a48SJohn Johansen 	}
72793c98a48SJohn Johansen 
72893c98a48SJohn Johansen 	return new;
72993c98a48SJohn Johansen }
73093c98a48SJohn Johansen 
profile_onexec(const struct cred * subj_cred,struct aa_profile * profile,struct aa_label * onexec,bool stack,const struct linux_binprm * bprm,char * buffer,struct path_cond * cond,bool * secure_exec)731*690f33e1SJohn Johansen static int profile_onexec(const struct cred *subj_cred,
732*690f33e1SJohn Johansen 			  struct aa_profile *profile, struct aa_label *onexec,
73393c98a48SJohn Johansen 			  bool stack, const struct linux_binprm *bprm,
73493c98a48SJohn Johansen 			  char *buffer, struct path_cond *cond,
73593c98a48SJohn Johansen 			  bool *secure_exec)
73693c98a48SJohn Johansen {
7371ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
7381ad22fccSJohn Johansen 						    typeof(*rules), list);
739217af7e2SJohn Johansen 	aa_state_t state = rules->file.start[AA_CLASS_FILE];
74093c98a48SJohn Johansen 	struct aa_perms perms = {};
74193c98a48SJohn Johansen 	const char *xname = NULL, *info = "change_profile onexec";
74293c98a48SJohn Johansen 	int error = -EACCES;
74393c98a48SJohn Johansen 
74493c98a48SJohn Johansen 	AA_BUG(!profile);
74593c98a48SJohn Johansen 	AA_BUG(!onexec);
74693c98a48SJohn Johansen 	AA_BUG(!bprm);
74793c98a48SJohn Johansen 	AA_BUG(!buffer);
74893c98a48SJohn Johansen 
74993c98a48SJohn Johansen 	if (profile_unconfined(profile)) {
75093c98a48SJohn Johansen 		/* change_profile on exec already granted */
75193c98a48SJohn Johansen 		/*
75293c98a48SJohn Johansen 		 * NOTE: Domain transitions from unconfined are allowed
75393c98a48SJohn Johansen 		 * even when no_new_privs is set because this aways results
75493c98a48SJohn Johansen 		 * in a further reduction of permissions.
75593c98a48SJohn Johansen 		 */
75693c98a48SJohn Johansen 		return 0;
75793c98a48SJohn Johansen 	}
75893c98a48SJohn Johansen 
75993c98a48SJohn Johansen 	error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
76093c98a48SJohn Johansen 			     &xname, &info, profile->disconnected);
76193c98a48SJohn Johansen 	if (error) {
76293c98a48SJohn Johansen 		if (profile_unconfined(profile) ||
76393c98a48SJohn Johansen 		    (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
76493c98a48SJohn Johansen 			AA_DEBUG("name lookup ix on error");
76593c98a48SJohn Johansen 			error = 0;
76693c98a48SJohn Johansen 		}
76793c98a48SJohn Johansen 		xname = bprm->filename;
76893c98a48SJohn Johansen 		goto audit;
76993c98a48SJohn Johansen 	}
77093c98a48SJohn Johansen 
77193c98a48SJohn Johansen 	/* find exec permissions for name */
772217af7e2SJohn Johansen 	state = aa_str_perms(&(rules->file), state, xname, cond, &perms);
77393c98a48SJohn Johansen 	if (!(perms.allow & AA_MAY_ONEXEC)) {
77493c98a48SJohn Johansen 		info = "no change_onexec valid for executable";
77593c98a48SJohn Johansen 		goto audit;
77693c98a48SJohn Johansen 	}
77793c98a48SJohn Johansen 	/* test if this exec can be paired with change_profile onexec.
77893c98a48SJohn Johansen 	 * onexec permission is linked to exec with a standard pairing
77993c98a48SJohn Johansen 	 * exec\0change_profile
78093c98a48SJohn Johansen 	 */
781217af7e2SJohn Johansen 	state = aa_dfa_null_transition(rules->file.dfa, state);
78293c98a48SJohn Johansen 	error = change_profile_perms(profile, onexec, stack, AA_MAY_ONEXEC,
78393c98a48SJohn Johansen 				     state, &perms);
78493c98a48SJohn Johansen 	if (error) {
78593c98a48SJohn Johansen 		perms.allow &= ~AA_MAY_ONEXEC;
78693c98a48SJohn Johansen 		goto audit;
78793c98a48SJohn Johansen 	}
78893c98a48SJohn Johansen 
78993c98a48SJohn Johansen 	if (!(perms.xindex & AA_X_UNSAFE)) {
79093c98a48SJohn Johansen 		if (DEBUG_ON) {
79193c98a48SJohn Johansen 			dbg_printk("apparmor: scrubbing environment "
79293c98a48SJohn Johansen 				   "variables for %s label=", xname);
7938ac2ca32SSebastian Andrzej Siewior 			aa_label_printk(onexec, GFP_KERNEL);
79493c98a48SJohn Johansen 			dbg_printk("\n");
79593c98a48SJohn Johansen 		}
79693c98a48SJohn Johansen 		*secure_exec = true;
79793c98a48SJohn Johansen 	}
79893c98a48SJohn Johansen 
79993c98a48SJohn Johansen audit:
800*690f33e1SJohn Johansen 	return aa_audit_file(subj_cred, profile, &perms, OP_EXEC,
801*690f33e1SJohn Johansen 			     AA_MAY_ONEXEC, xname,
80293c98a48SJohn Johansen 			     NULL, onexec, cond->uid, info, error);
80393c98a48SJohn Johansen }
80493c98a48SJohn Johansen 
80593c98a48SJohn Johansen /* ensure none ns domain transitions are correctly applied with onexec */
80693c98a48SJohn Johansen 
handle_onexec(const struct cred * subj_cred,struct aa_label * label,struct aa_label * onexec,bool stack,const struct linux_binprm * bprm,char * buffer,struct path_cond * cond,bool * unsafe)807*690f33e1SJohn Johansen static struct aa_label *handle_onexec(const struct cred *subj_cred,
808*690f33e1SJohn Johansen 				      struct aa_label *label,
80993c98a48SJohn Johansen 				      struct aa_label *onexec, bool stack,
81093c98a48SJohn Johansen 				      const struct linux_binprm *bprm,
81193c98a48SJohn Johansen 				      char *buffer, struct path_cond *cond,
81293c98a48SJohn Johansen 				      bool *unsafe)
81393c98a48SJohn Johansen {
81493c98a48SJohn Johansen 	struct aa_profile *profile;
81593c98a48SJohn Johansen 	struct aa_label *new;
81693c98a48SJohn Johansen 	int error;
81793c98a48SJohn Johansen 
81893c98a48SJohn Johansen 	AA_BUG(!label);
81993c98a48SJohn Johansen 	AA_BUG(!onexec);
82093c98a48SJohn Johansen 	AA_BUG(!bprm);
82193c98a48SJohn Johansen 	AA_BUG(!buffer);
82293c98a48SJohn Johansen 
82393c98a48SJohn Johansen 	if (!stack) {
82493c98a48SJohn Johansen 		error = fn_for_each_in_ns(label, profile,
825*690f33e1SJohn Johansen 				profile_onexec(subj_cred, profile, onexec, stack,
82693c98a48SJohn Johansen 					       bprm, buffer, cond, unsafe));
82793c98a48SJohn Johansen 		if (error)
82893c98a48SJohn Johansen 			return ERR_PTR(error);
8298ac2ca32SSebastian Andrzej Siewior 		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
83093c98a48SJohn Johansen 				aa_get_newest_label(onexec),
831*690f33e1SJohn Johansen 				profile_transition(subj_cred, profile, bprm,
832*690f33e1SJohn Johansen 						   buffer,
83393c98a48SJohn Johansen 						   cond, unsafe));
83493c98a48SJohn Johansen 
83593c98a48SJohn Johansen 	} else {
836b2c2086cSZygmunt Krynicki 		/* TODO: determine how much we want to loosen this */
83793c98a48SJohn Johansen 		error = fn_for_each_in_ns(label, profile,
838*690f33e1SJohn Johansen 				profile_onexec(subj_cred, profile, onexec, stack, bprm,
83993c98a48SJohn Johansen 					       buffer, cond, unsafe));
84093c98a48SJohn Johansen 		if (error)
84193c98a48SJohn Johansen 			return ERR_PTR(error);
8428ac2ca32SSebastian Andrzej Siewior 		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
84393c98a48SJohn Johansen 				aa_label_merge(&profile->label, onexec,
8448ac2ca32SSebastian Andrzej Siewior 					       GFP_KERNEL),
845*690f33e1SJohn Johansen 				profile_transition(subj_cred, profile, bprm,
846*690f33e1SJohn Johansen 						   buffer,
84793c98a48SJohn Johansen 						   cond, unsafe));
84893c98a48SJohn Johansen 	}
84993c98a48SJohn Johansen 
85093c98a48SJohn Johansen 	if (new)
85193c98a48SJohn Johansen 		return new;
85293c98a48SJohn Johansen 
85393c98a48SJohn Johansen 	/* TODO: get rid of GLOBAL_ROOT_UID */
85493c98a48SJohn Johansen 	error = fn_for_each_in_ns(label, profile,
855*690f33e1SJohn Johansen 			aa_audit_file(subj_cred, profile, &nullperms,
856*690f33e1SJohn Johansen 				      OP_CHANGE_ONEXEC,
85793c98a48SJohn Johansen 				      AA_MAY_ONEXEC, bprm->filename, NULL,
85893c98a48SJohn Johansen 				      onexec, GLOBAL_ROOT_UID,
85993c98a48SJohn Johansen 				      "failed to build target label", -ENOMEM));
86093c98a48SJohn Johansen 	return ERR_PTR(error);
861898127c3SJohn Johansen }
862898127c3SJohn Johansen 
863898127c3SJohn Johansen /**
864b8bff599SEric W. Biederman  * apparmor_bprm_creds_for_exec - Update the new creds on the bprm struct
865898127c3SJohn Johansen  * @bprm: binprm for the exec  (NOT NULL)
866898127c3SJohn Johansen  *
867898127c3SJohn Johansen  * Returns: %0 or error on failure
86893c98a48SJohn Johansen  *
86993c98a48SJohn Johansen  * TODO: once the other paths are done see if we can't refactor into a fn
870898127c3SJohn Johansen  */
apparmor_bprm_creds_for_exec(struct linux_binprm * bprm)871b8bff599SEric W. Biederman int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
872898127c3SJohn Johansen {
873f175221aSJohn Johansen 	struct aa_task_ctx *ctx;
87493c98a48SJohn Johansen 	struct aa_label *label, *new = NULL;
875*690f33e1SJohn Johansen 	const struct cred *subj_cred;
87693c98a48SJohn Johansen 	struct aa_profile *profile;
877898127c3SJohn Johansen 	char *buffer = NULL;
87893c98a48SJohn Johansen 	const char *info = NULL;
87993c98a48SJohn Johansen 	int error = 0;
88093c98a48SJohn Johansen 	bool unsafe = false;
881e67fe633SChristian Brauner 	vfsuid_t vfsuid = i_uid_into_vfsuid(file_mnt_idmap(bprm->file),
8823cee6079SChristian Brauner 					    file_inode(bprm->file));
883898127c3SJohn Johansen 	struct path_cond cond = {
8845e26a01eSChristian Brauner 		vfsuid_into_kuid(vfsuid),
885496ad9aaSAl Viro 		file_inode(bprm->file)->i_mode
886898127c3SJohn Johansen 	};
887898127c3SJohn Johansen 
888*690f33e1SJohn Johansen 	subj_cred = current_cred();
889de62de59SJohn Johansen 	ctx = task_ctx(current);
890d9087c49SJohn Johansen 	AA_BUG(!cred_label(bprm->cred));
891f175221aSJohn Johansen 	AA_BUG(!ctx);
892898127c3SJohn Johansen 
893d9087c49SJohn Johansen 	label = aa_get_newest_label(cred_label(bprm->cred));
8944227c333SJohn Johansen 
8959fcf78ccSJohn Johansen 	/*
8969fcf78ccSJohn Johansen 	 * Detect no new privs being set, and store the label it
8979fcf78ccSJohn Johansen 	 * occurred under. Ideally this would happen when nnp
8989fcf78ccSJohn Johansen 	 * is set but there isn't a good way to do that yet.
8999fcf78ccSJohn Johansen 	 *
9009fcf78ccSJohn Johansen 	 * Testing for unconfined must be done before the subset test
9019fcf78ccSJohn Johansen 	 */
9029fcf78ccSJohn Johansen 	if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && !unconfined(label) &&
9039fcf78ccSJohn Johansen 	    !ctx->nnp)
9049fcf78ccSJohn Johansen 		ctx->nnp = aa_get_label(label);
9059fcf78ccSJohn Johansen 
9064227c333SJohn Johansen 	/* buffer freed below, name is pointer into buffer */
907341c1fdaSJohn Johansen 	buffer = aa_get_buffer(false);
908df323337SSebastian Andrzej Siewior 	if (!buffer) {
909df323337SSebastian Andrzej Siewior 		error = -ENOMEM;
910df323337SSebastian Andrzej Siewior 		goto done;
911df323337SSebastian Andrzej Siewior 	}
912df323337SSebastian Andrzej Siewior 
91393c98a48SJohn Johansen 	/* Test for onexec first as onexec override other x transitions. */
914f175221aSJohn Johansen 	if (ctx->onexec)
915*690f33e1SJohn Johansen 		new = handle_onexec(subj_cred, label, ctx->onexec, ctx->token,
91693c98a48SJohn Johansen 				    bprm, buffer, &cond, &unsafe);
917898127c3SJohn Johansen 	else
9188ac2ca32SSebastian Andrzej Siewior 		new = fn_label_build(label, profile, GFP_KERNEL,
919*690f33e1SJohn Johansen 				profile_transition(subj_cred, profile, bprm,
920*690f33e1SJohn Johansen 						   buffer,
92193c98a48SJohn Johansen 						   &cond, &unsafe));
922898127c3SJohn Johansen 
92393c98a48SJohn Johansen 	AA_BUG(!new);
92493c98a48SJohn Johansen 	if (IS_ERR(new)) {
92593c98a48SJohn Johansen 		error = PTR_ERR(new);
92693c98a48SJohn Johansen 		goto done;
92793c98a48SJohn Johansen 	} else if (!new) {
928898127c3SJohn Johansen 		error = -ENOMEM;
92993c98a48SJohn Johansen 		goto done;
930c29bceb3SJohn Johansen 	}
931c29bceb3SJohn Johansen 
9329fcf78ccSJohn Johansen 	/* Policy has specified a domain transitions. If no_new_privs and
9339fcf78ccSJohn Johansen 	 * confined ensure the transition is to confinement that is subset
9349fcf78ccSJohn Johansen 	 * of the confinement when the task entered no new privs.
9359fcf78ccSJohn Johansen 	 *
9369fcf78ccSJohn Johansen 	 * NOTE: Domain transitions from unconfined and to stacked
9379fcf78ccSJohn Johansen 	 * subsets are allowed even when no_new_privs is set because this
9389fcf78ccSJohn Johansen 	 * aways results in a further reduction of permissions.
9399fcf78ccSJohn Johansen 	 */
9409fcf78ccSJohn Johansen 	if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) &&
9413ed4aaa9SJohn Johansen 	    !unconfined(label) &&
9423ed4aaa9SJohn Johansen 	    !aa_label_is_unconfined_subset(new, ctx->nnp)) {
9439fcf78ccSJohn Johansen 		error = -EPERM;
9449fcf78ccSJohn Johansen 		info = "no new privs";
9459fcf78ccSJohn Johansen 		goto audit;
9469fcf78ccSJohn Johansen 	}
947898127c3SJohn Johansen 
948898127c3SJohn Johansen 	if (bprm->unsafe & LSM_UNSAFE_SHARE) {
949898127c3SJohn Johansen 		/* FIXME: currently don't mediate shared state */
950898127c3SJohn Johansen 		;
951898127c3SJohn Johansen 	}
952898127c3SJohn Johansen 
95393c98a48SJohn Johansen 	if (bprm->unsafe & (LSM_UNSAFE_PTRACE)) {
95493c98a48SJohn Johansen 		/* TODO: test needs to be profile of label to new */
955*690f33e1SJohn Johansen 		error = may_change_ptraced_domain(bprm->cred, new, &info);
956f7da2de0SJohn Johansen 		if (error)
957898127c3SJohn Johansen 			goto audit;
958898127c3SJohn Johansen 	}
959898127c3SJohn Johansen 
96093c98a48SJohn Johansen 	if (unsafe) {
96193c98a48SJohn Johansen 		if (DEBUG_ON) {
96293c98a48SJohn Johansen 			dbg_printk("scrubbing environment variables for %s "
96393c98a48SJohn Johansen 				   "label=", bprm->filename);
9648ac2ca32SSebastian Andrzej Siewior 			aa_label_printk(new, GFP_KERNEL);
96593c98a48SJohn Johansen 			dbg_printk("\n");
96693c98a48SJohn Johansen 		}
967993b3ab0SKees Cook 		bprm->secureexec = 1;
968898127c3SJohn Johansen 	}
96993c98a48SJohn Johansen 
97093c98a48SJohn Johansen 	if (label->proxy != new->proxy) {
97193c98a48SJohn Johansen 		/* when transitioning clear unsafe personality bits */
97293c98a48SJohn Johansen 		if (DEBUG_ON) {
97393c98a48SJohn Johansen 			dbg_printk("apparmor: clearing unsafe personality "
97493c98a48SJohn Johansen 				   "bits. %s label=", bprm->filename);
9758ac2ca32SSebastian Andrzej Siewior 			aa_label_printk(new, GFP_KERNEL);
97693c98a48SJohn Johansen 			dbg_printk("\n");
97793c98a48SJohn Johansen 		}
978898127c3SJohn Johansen 		bprm->per_clear |= PER_CLEAR_ON_SETID;
97993c98a48SJohn Johansen 	}
980d9087c49SJohn Johansen 	aa_put_label(cred_label(bprm->cred));
981d9087c49SJohn Johansen 	/* transfer reference, released when cred is freed */
98269b5a44aSCasey Schaufler 	set_cred_label(bprm->cred, new);
983898127c3SJohn Johansen 
98493c98a48SJohn Johansen done:
985637f688dSJohn Johansen 	aa_put_label(label);
986df323337SSebastian Andrzej Siewior 	aa_put_buffer(buffer);
987898127c3SJohn Johansen 
988898127c3SJohn Johansen 	return error;
98993c98a48SJohn Johansen 
99093c98a48SJohn Johansen audit:
99193c98a48SJohn Johansen 	error = fn_for_each(label, profile,
992*690f33e1SJohn Johansen 			aa_audit_file(current_cred(), profile, &nullperms,
993*690f33e1SJohn Johansen 				      OP_EXEC, MAY_EXEC,
99493c98a48SJohn Johansen 				      bprm->filename, NULL, new,
9955e26a01eSChristian Brauner 				      vfsuid_into_kuid(vfsuid), info, error));
99693c98a48SJohn Johansen 	aa_put_label(new);
99793c98a48SJohn Johansen 	goto done;
998898127c3SJohn Johansen }
999898127c3SJohn Johansen 
1000898127c3SJohn Johansen /*
1001898127c3SJohn Johansen  * Functions for self directed profile change
1002898127c3SJohn Johansen  */
1003898127c3SJohn Johansen 
100489dbf196SJohn Johansen 
100589dbf196SJohn Johansen /* helper fn for change_hat
1006898127c3SJohn Johansen  *
100789dbf196SJohn Johansen  * Returns: label for hat transition OR ERR_PTR.  Does NOT return NULL
1008898127c3SJohn Johansen  */
build_change_hat(const struct cred * subj_cred,struct aa_profile * profile,const char * name,bool sibling)1009*690f33e1SJohn Johansen static struct aa_label *build_change_hat(const struct cred *subj_cred,
1010*690f33e1SJohn Johansen 					 struct aa_profile *profile,
101189dbf196SJohn Johansen 					 const char *name, bool sibling)
1012898127c3SJohn Johansen {
101389dbf196SJohn Johansen 	struct aa_profile *root, *hat = NULL;
101489dbf196SJohn Johansen 	const char *info = NULL;
101589dbf196SJohn Johansen 	int error = 0;
101689dbf196SJohn Johansen 
101789dbf196SJohn Johansen 	if (sibling && PROFILE_IS_HAT(profile)) {
101889dbf196SJohn Johansen 		root = aa_get_profile_rcu(&profile->parent);
101989dbf196SJohn Johansen 	} else if (!sibling && !PROFILE_IS_HAT(profile)) {
102089dbf196SJohn Johansen 		root = aa_get_profile(profile);
102189dbf196SJohn Johansen 	} else {
102289dbf196SJohn Johansen 		info = "conflicting target types";
102389dbf196SJohn Johansen 		error = -EPERM;
102489dbf196SJohn Johansen 		goto audit;
102589dbf196SJohn Johansen 	}
102689dbf196SJohn Johansen 
102789dbf196SJohn Johansen 	hat = aa_find_child(root, name);
102889dbf196SJohn Johansen 	if (!hat) {
102989dbf196SJohn Johansen 		error = -ENOENT;
103089dbf196SJohn Johansen 		if (COMPLAIN_MODE(profile)) {
103158f89ce5SJohn Johansen 			hat = aa_new_learning_profile(profile, true, name,
103289dbf196SJohn Johansen 						      GFP_KERNEL);
103389dbf196SJohn Johansen 			if (!hat) {
103489dbf196SJohn Johansen 				info = "failed null profile create";
103589dbf196SJohn Johansen 				error = -ENOMEM;
103689dbf196SJohn Johansen 			}
103789dbf196SJohn Johansen 		}
103889dbf196SJohn Johansen 	}
103989dbf196SJohn Johansen 	aa_put_profile(root);
104089dbf196SJohn Johansen 
104189dbf196SJohn Johansen audit:
1042*690f33e1SJohn Johansen 	aa_audit_file(subj_cred, profile, &nullperms, OP_CHANGE_HAT,
1043*690f33e1SJohn Johansen 		      AA_MAY_CHANGEHAT,
104489dbf196SJohn Johansen 		      name, hat ? hat->base.hname : NULL,
104524b87a16SJohn Johansen 		      hat ? &hat->label : NULL, GLOBAL_ROOT_UID, info,
104689dbf196SJohn Johansen 		      error);
104789dbf196SJohn Johansen 	if (!hat || (error && error != -ENOENT))
104889dbf196SJohn Johansen 		return ERR_PTR(error);
104989dbf196SJohn Johansen 	/* if hat && error - complain mode, already audited and we adjust for
105089dbf196SJohn Johansen 	 * complain mode allow by returning hat->label
105189dbf196SJohn Johansen 	 */
105289dbf196SJohn Johansen 	return &hat->label;
105389dbf196SJohn Johansen }
105489dbf196SJohn Johansen 
105589dbf196SJohn Johansen /* helper fn for changing into a hat
105689dbf196SJohn Johansen  *
105789dbf196SJohn Johansen  * Returns: label for hat transition or ERR_PTR. Does not return NULL
105889dbf196SJohn Johansen  */
change_hat(const struct cred * subj_cred,struct aa_label * label,const char * hats[],int count,int flags)1059*690f33e1SJohn Johansen static struct aa_label *change_hat(const struct cred *subj_cred,
1060*690f33e1SJohn Johansen 				   struct aa_label *label, const char *hats[],
106189dbf196SJohn Johansen 				   int count, int flags)
106289dbf196SJohn Johansen {
106389dbf196SJohn Johansen 	struct aa_profile *profile, *root, *hat = NULL;
106489dbf196SJohn Johansen 	struct aa_label *new;
106589dbf196SJohn Johansen 	struct label_it it;
106689dbf196SJohn Johansen 	bool sibling = false;
106789dbf196SJohn Johansen 	const char *name, *info = NULL;
106889dbf196SJohn Johansen 	int i, error;
106989dbf196SJohn Johansen 
107089dbf196SJohn Johansen 	AA_BUG(!label);
107189dbf196SJohn Johansen 	AA_BUG(!hats);
107289dbf196SJohn Johansen 	AA_BUG(count < 1);
107389dbf196SJohn Johansen 
107489dbf196SJohn Johansen 	if (PROFILE_IS_HAT(labels_profile(label)))
107589dbf196SJohn Johansen 		sibling = true;
107689dbf196SJohn Johansen 
107789dbf196SJohn Johansen 	/*find first matching hat */
107889dbf196SJohn Johansen 	for (i = 0; i < count && !hat; i++) {
107989dbf196SJohn Johansen 		name = hats[i];
108089dbf196SJohn Johansen 		label_for_each_in_ns(it, labels_ns(label), label, profile) {
108189dbf196SJohn Johansen 			if (sibling && PROFILE_IS_HAT(profile)) {
108289dbf196SJohn Johansen 				root = aa_get_profile_rcu(&profile->parent);
108389dbf196SJohn Johansen 			} else if (!sibling && !PROFILE_IS_HAT(profile)) {
108489dbf196SJohn Johansen 				root = aa_get_profile(profile);
108589dbf196SJohn Johansen 			} else {	/* conflicting change type */
108689dbf196SJohn Johansen 				info = "conflicting targets types";
108789dbf196SJohn Johansen 				error = -EPERM;
108889dbf196SJohn Johansen 				goto fail;
108989dbf196SJohn Johansen 			}
109089dbf196SJohn Johansen 			hat = aa_find_child(root, name);
109189dbf196SJohn Johansen 			aa_put_profile(root);
109289dbf196SJohn Johansen 			if (!hat) {
109389dbf196SJohn Johansen 				if (!COMPLAIN_MODE(profile))
109489dbf196SJohn Johansen 					goto outer_continue;
109589dbf196SJohn Johansen 				/* complain mode succeed as if hat */
109689dbf196SJohn Johansen 			} else if (!PROFILE_IS_HAT(hat)) {
109789dbf196SJohn Johansen 				info = "target not hat";
109889dbf196SJohn Johansen 				error = -EPERM;
109989dbf196SJohn Johansen 				aa_put_profile(hat);
110089dbf196SJohn Johansen 				goto fail;
110189dbf196SJohn Johansen 			}
110289dbf196SJohn Johansen 			aa_put_profile(hat);
110389dbf196SJohn Johansen 		}
110489dbf196SJohn Johansen 		/* found a hat for all profiles in ns */
110589dbf196SJohn Johansen 		goto build;
110689dbf196SJohn Johansen outer_continue:
110789dbf196SJohn Johansen 	;
110889dbf196SJohn Johansen 	}
110989dbf196SJohn Johansen 	/* no hats that match, find appropriate error
111089dbf196SJohn Johansen 	 *
111189dbf196SJohn Johansen 	 * In complain mode audit of the failure is based off of the first
111289dbf196SJohn Johansen 	 * hat supplied.  This is done due how userspace interacts with
111389dbf196SJohn Johansen 	 * change_hat.
111489dbf196SJohn Johansen 	 */
111589dbf196SJohn Johansen 	name = NULL;
111689dbf196SJohn Johansen 	label_for_each_in_ns(it, labels_ns(label), label, profile) {
111789dbf196SJohn Johansen 		if (!list_empty(&profile->base.profiles)) {
111889dbf196SJohn Johansen 			info = "hat not found";
111989dbf196SJohn Johansen 			error = -ENOENT;
112089dbf196SJohn Johansen 			goto fail;
112189dbf196SJohn Johansen 		}
112289dbf196SJohn Johansen 	}
112389dbf196SJohn Johansen 	info = "no hats defined";
112489dbf196SJohn Johansen 	error = -ECHILD;
112589dbf196SJohn Johansen 
112689dbf196SJohn Johansen fail:
112789dbf196SJohn Johansen 	label_for_each_in_ns(it, labels_ns(label), label, profile) {
112889dbf196SJohn Johansen 		/*
112989dbf196SJohn Johansen 		 * no target as it has failed to be found or built
113089dbf196SJohn Johansen 		 *
113189dbf196SJohn Johansen 		 * change_hat uses probing and should not log failures
113289dbf196SJohn Johansen 		 * related to missing hats
113389dbf196SJohn Johansen 		 */
113489dbf196SJohn Johansen 		/* TODO: get rid of GLOBAL_ROOT_UID */
113589dbf196SJohn Johansen 		if (count > 1 || COMPLAIN_MODE(profile)) {
1136*690f33e1SJohn Johansen 			aa_audit_file(subj_cred, profile, &nullperms,
1137*690f33e1SJohn Johansen 				      OP_CHANGE_HAT,
113889dbf196SJohn Johansen 				      AA_MAY_CHANGEHAT, name, NULL, NULL,
113989dbf196SJohn Johansen 				      GLOBAL_ROOT_UID, info, error);
114089dbf196SJohn Johansen 		}
114189dbf196SJohn Johansen 	}
114289dbf196SJohn Johansen 	return ERR_PTR(error);
114389dbf196SJohn Johansen 
114489dbf196SJohn Johansen build:
114589dbf196SJohn Johansen 	new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
1146*690f33e1SJohn Johansen 				   build_change_hat(subj_cred, profile, name,
1147*690f33e1SJohn Johansen 						    sibling),
114889dbf196SJohn Johansen 				   aa_get_label(&profile->label));
114989dbf196SJohn Johansen 	if (!new) {
115089dbf196SJohn Johansen 		info = "label build failed";
115189dbf196SJohn Johansen 		error = -ENOMEM;
115289dbf196SJohn Johansen 		goto fail;
115389dbf196SJohn Johansen 	} /* else if (IS_ERR) build_change_hat has logged error so return new */
115489dbf196SJohn Johansen 
115589dbf196SJohn Johansen 	return new;
1156898127c3SJohn Johansen }
1157898127c3SJohn Johansen 
1158898127c3SJohn Johansen /**
1159898127c3SJohn Johansen  * aa_change_hat - change hat to/from subprofile
1160898127c3SJohn Johansen  * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0)
1161898127c3SJohn Johansen  * @count: number of hat names in @hats
1162898127c3SJohn Johansen  * @token: magic value to validate the hat change
1163df8073c6SJohn Johansen  * @flags: flags affecting behavior of the change
1164898127c3SJohn Johansen  *
116589dbf196SJohn Johansen  * Returns %0 on success, error otherwise.
116689dbf196SJohn Johansen  *
1167898127c3SJohn Johansen  * Change to the first profile specified in @hats that exists, and store
1168898127c3SJohn Johansen  * the @hat_magic in the current task context.  If the count == 0 and the
1169898127c3SJohn Johansen  * @token matches that stored in the current task context, return to the
1170898127c3SJohn Johansen  * top level profile.
1171898127c3SJohn Johansen  *
117289dbf196SJohn Johansen  * change_hat only applies to profiles in the current ns, and each profile
117389dbf196SJohn Johansen  * in the ns must make the same transition otherwise change_hat will fail.
1174898127c3SJohn Johansen  */
aa_change_hat(const char * hats[],int count,u64 token,int flags)1175df8073c6SJohn Johansen int aa_change_hat(const char *hats[], int count, u64 token, int flags)
1176898127c3SJohn Johansen {
1177*690f33e1SJohn Johansen 	const struct cred *subj_cred;
11789fcf78ccSJohn Johansen 	struct aa_task_ctx *ctx = task_ctx(current);
117989dbf196SJohn Johansen 	struct aa_label *label, *previous, *new = NULL, *target = NULL;
118089dbf196SJohn Johansen 	struct aa_profile *profile;
11812d679f3cSJohn Johansen 	struct aa_perms perms = {};
118289dbf196SJohn Johansen 	const char *info = NULL;
1183898127c3SJohn Johansen 	int error = 0;
1184898127c3SJohn Johansen 
1185898127c3SJohn Johansen 	/* released below */
1186*690f33e1SJohn Johansen 	subj_cred = get_current_cred();
1187*690f33e1SJohn Johansen 	label = aa_get_newest_cred_label(subj_cred);
1188f175221aSJohn Johansen 	previous = aa_get_newest_label(ctx->previous);
1189898127c3SJohn Johansen 
11909fcf78ccSJohn Johansen 	/*
11919fcf78ccSJohn Johansen 	 * Detect no new privs being set, and store the label it
11929fcf78ccSJohn Johansen 	 * occurred under. Ideally this would happen when nnp
11939fcf78ccSJohn Johansen 	 * is set but there isn't a good way to do that yet.
11949fcf78ccSJohn Johansen 	 *
11959fcf78ccSJohn Johansen 	 * Testing for unconfined must be done before the subset test
11969fcf78ccSJohn Johansen 	 */
11979fcf78ccSJohn Johansen 	if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
11989fcf78ccSJohn Johansen 		ctx->nnp = aa_get_label(label);
11999fcf78ccSJohn Johansen 
1200637f688dSJohn Johansen 	if (unconfined(label)) {
120189dbf196SJohn Johansen 		info = "unconfined can not change_hat";
1202898127c3SJohn Johansen 		error = -EPERM;
120389dbf196SJohn Johansen 		goto fail;
1204898127c3SJohn Johansen 	}
1205898127c3SJohn Johansen 
1206898127c3SJohn Johansen 	if (count) {
1207*690f33e1SJohn Johansen 		new = change_hat(subj_cred, label, hats, count, flags);
120889dbf196SJohn Johansen 		AA_BUG(!new);
120989dbf196SJohn Johansen 		if (IS_ERR(new)) {
121089dbf196SJohn Johansen 			error = PTR_ERR(new);
121189dbf196SJohn Johansen 			new = NULL;
121289dbf196SJohn Johansen 			/* already audited */
1213898127c3SJohn Johansen 			goto out;
1214898127c3SJohn Johansen 		}
1215898127c3SJohn Johansen 
1216*690f33e1SJohn Johansen 		/* target cred is the same as current except new label */
1217*690f33e1SJohn Johansen 		error = may_change_ptraced_domain(subj_cred, new, &info);
121889dbf196SJohn Johansen 		if (error)
121989dbf196SJohn Johansen 			goto fail;
1220898127c3SJohn Johansen 
12219fcf78ccSJohn Johansen 		/*
12229fcf78ccSJohn Johansen 		 * no new privs prevents domain transitions that would
12239fcf78ccSJohn Johansen 		 * reduce restrictions.
12249fcf78ccSJohn Johansen 		 */
12259fcf78ccSJohn Johansen 		if (task_no_new_privs(current) && !unconfined(label) &&
12263ed4aaa9SJohn Johansen 		    !aa_label_is_unconfined_subset(new, ctx->nnp)) {
12279fcf78ccSJohn Johansen 			/* not an apparmor denial per se, so don't log it */
12289fcf78ccSJohn Johansen 			AA_DEBUG("no_new_privs - change_hat denied");
12299fcf78ccSJohn Johansen 			error = -EPERM;
12309fcf78ccSJohn Johansen 			goto out;
12319fcf78ccSJohn Johansen 		}
12329fcf78ccSJohn Johansen 
123389dbf196SJohn Johansen 		if (flags & AA_CHANGE_TEST)
123489dbf196SJohn Johansen 			goto out;
1235898127c3SJohn Johansen 
123689dbf196SJohn Johansen 		target = new;
123789dbf196SJohn Johansen 		error = aa_set_current_hat(new, token);
1238898127c3SJohn Johansen 		if (error == -EACCES)
1239898127c3SJohn Johansen 			/* kill task in case of brute force attacks */
124089dbf196SJohn Johansen 			goto kill;
124189dbf196SJohn Johansen 	} else if (previous && !(flags & AA_CHANGE_TEST)) {
12429fcf78ccSJohn Johansen 		/*
12439fcf78ccSJohn Johansen 		 * no new privs prevents domain transitions that would
12449fcf78ccSJohn Johansen 		 * reduce restrictions.
12459fcf78ccSJohn Johansen 		 */
12469fcf78ccSJohn Johansen 		if (task_no_new_privs(current) && !unconfined(label) &&
12473ed4aaa9SJohn Johansen 		    !aa_label_is_unconfined_subset(previous, ctx->nnp)) {
12489fcf78ccSJohn Johansen 			/* not an apparmor denial per se, so don't log it */
12499fcf78ccSJohn Johansen 			AA_DEBUG("no_new_privs - change_hat denied");
12509fcf78ccSJohn Johansen 			error = -EPERM;
12519fcf78ccSJohn Johansen 			goto out;
12529fcf78ccSJohn Johansen 		}
12539fcf78ccSJohn Johansen 
125489dbf196SJohn Johansen 		/* Return to saved label.  Kill task if restore fails
1255898127c3SJohn Johansen 		 * to avoid brute force attacks
1256898127c3SJohn Johansen 		 */
125789dbf196SJohn Johansen 		target = previous;
1258637f688dSJohn Johansen 		error = aa_restore_previous_label(token);
125989dbf196SJohn Johansen 		if (error) {
126089dbf196SJohn Johansen 			if (error == -EACCES)
126189dbf196SJohn Johansen 				goto kill;
126289dbf196SJohn Johansen 			goto fail;
126389dbf196SJohn Johansen 		}
126489dbf196SJohn Johansen 	} /* else ignore @flags && restores when there is no saved profile */
1265898127c3SJohn Johansen 
1266898127c3SJohn Johansen out:
126789dbf196SJohn Johansen 	aa_put_label(new);
126889dbf196SJohn Johansen 	aa_put_label(previous);
1269637f688dSJohn Johansen 	aa_put_label(label);
1270*690f33e1SJohn Johansen 	put_cred(subj_cred);
1271898127c3SJohn Johansen 
1272898127c3SJohn Johansen 	return error;
127389dbf196SJohn Johansen 
127489dbf196SJohn Johansen kill:
127589dbf196SJohn Johansen 	info = "failed token match";
127689dbf196SJohn Johansen 	perms.kill = AA_MAY_CHANGEHAT;
127789dbf196SJohn Johansen 
127889dbf196SJohn Johansen fail:
127989dbf196SJohn Johansen 	fn_for_each_in_ns(label, profile,
1280*690f33e1SJohn Johansen 		aa_audit_file(subj_cred, profile, &perms, OP_CHANGE_HAT,
128189dbf196SJohn Johansen 			      AA_MAY_CHANGEHAT, NULL, NULL, target,
128289dbf196SJohn Johansen 			      GLOBAL_ROOT_UID, info, error));
128389dbf196SJohn Johansen 
128489dbf196SJohn Johansen 	goto out;
1285898127c3SJohn Johansen }
1286898127c3SJohn Johansen 
128789dbf196SJohn Johansen 
change_profile_perms_wrapper(const char * op,const char * name,const struct cred * subj_cred,struct aa_profile * profile,struct aa_label * target,bool stack,u32 request,struct aa_perms * perms)1288e00b02bbSJohn Johansen static int change_profile_perms_wrapper(const char *op, const char *name,
1289*690f33e1SJohn Johansen 					const struct cred *subj_cred,
1290e00b02bbSJohn Johansen 					struct aa_profile *profile,
1291e00b02bbSJohn Johansen 					struct aa_label *target, bool stack,
1292e00b02bbSJohn Johansen 					u32 request, struct aa_perms *perms)
1293e00b02bbSJohn Johansen {
12941ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
12951ad22fccSJohn Johansen 						    typeof(*rules), list);
1296e00b02bbSJohn Johansen 	const char *info = NULL;
1297e00b02bbSJohn Johansen 	int error = 0;
1298e00b02bbSJohn Johansen 
1299e00b02bbSJohn Johansen 	if (!error)
1300e00b02bbSJohn Johansen 		error = change_profile_perms(profile, target, stack, request,
1301217af7e2SJohn Johansen 					     rules->file.start[AA_CLASS_FILE],
130253bdc46fSJohn Johansen 					     perms);
1303e00b02bbSJohn Johansen 	if (error)
1304*690f33e1SJohn Johansen 		error = aa_audit_file(subj_cred, profile, perms, op, request,
1305*690f33e1SJohn Johansen 				      name,
1306e00b02bbSJohn Johansen 				      NULL, target, GLOBAL_ROOT_UID, info,
1307e00b02bbSJohn Johansen 				      error);
1308e00b02bbSJohn Johansen 
1309e00b02bbSJohn Johansen 	return error;
1310e00b02bbSJohn Johansen }
131189dbf196SJohn Johansen 
1312898127c3SJohn Johansen /**
1313898127c3SJohn Johansen  * aa_change_profile - perform a one-way profile transition
1314aa9a39adSJohn Johansen  * @fqname: name of profile may include namespace (NOT NULL)
1315df8073c6SJohn Johansen  * @flags: flags affecting change behavior
1316898127c3SJohn Johansen  *
1317898127c3SJohn Johansen  * Change to new profile @name.  Unlike with hats, there is no way
1318898127c3SJohn Johansen  * to change back.  If @name isn't specified the current profile name is
1319898127c3SJohn Johansen  * used.
1320898127c3SJohn Johansen  * If @onexec then the transition is delayed until
1321898127c3SJohn Johansen  * the next exec.
1322898127c3SJohn Johansen  *
1323898127c3SJohn Johansen  * Returns %0 on success, error otherwise.
1324898127c3SJohn Johansen  */
aa_change_profile(const char * fqname,int flags)1325df8073c6SJohn Johansen int aa_change_profile(const char *fqname, int flags)
1326898127c3SJohn Johansen {
1327e00b02bbSJohn Johansen 	struct aa_label *label, *new = NULL, *target = NULL;
1328e00b02bbSJohn Johansen 	struct aa_profile *profile;
13292d679f3cSJohn Johansen 	struct aa_perms perms = {};
1330e00b02bbSJohn Johansen 	const char *info = NULL;
1331e00b02bbSJohn Johansen 	const char *auditname = fqname;		/* retain leading & if stack */
1332e00b02bbSJohn Johansen 	bool stack = flags & AA_CHANGE_STACK;
13339fcf78ccSJohn Johansen 	struct aa_task_ctx *ctx = task_ctx(current);
1334*690f33e1SJohn Johansen 	const struct cred *subj_cred = get_current_cred();
133547f6e5ccSJohn Johansen 	int error = 0;
1336e00b02bbSJohn Johansen 	char *op;
1337898127c3SJohn Johansen 	u32 request;
1338898127c3SJohn Johansen 
13399fcf78ccSJohn Johansen 	label = aa_get_current_label();
13409fcf78ccSJohn Johansen 
13419fcf78ccSJohn Johansen 	/*
13429fcf78ccSJohn Johansen 	 * Detect no new privs being set, and store the label it
13439fcf78ccSJohn Johansen 	 * occurred under. Ideally this would happen when nnp
13449fcf78ccSJohn Johansen 	 * is set but there isn't a good way to do that yet.
13459fcf78ccSJohn Johansen 	 *
13469fcf78ccSJohn Johansen 	 * Testing for unconfined must be done before the subset test
13479fcf78ccSJohn Johansen 	 */
13489fcf78ccSJohn Johansen 	if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
13499fcf78ccSJohn Johansen 		ctx->nnp = aa_get_label(label);
13509fcf78ccSJohn Johansen 
1351aa9a39adSJohn Johansen 	if (!fqname || !*fqname) {
1352a0b845ffSXiyu Yang 		aa_put_label(label);
1353aa9a39adSJohn Johansen 		AA_DEBUG("no profile name");
1354898127c3SJohn Johansen 		return -EINVAL;
1355aa9a39adSJohn Johansen 	}
1356898127c3SJohn Johansen 
1357df8073c6SJohn Johansen 	if (flags & AA_CHANGE_ONEXEC) {
1358898127c3SJohn Johansen 		request = AA_MAY_ONEXEC;
1359e00b02bbSJohn Johansen 		if (stack)
1360e00b02bbSJohn Johansen 			op = OP_STACK_ONEXEC;
1361e00b02bbSJohn Johansen 		else
1362898127c3SJohn Johansen 			op = OP_CHANGE_ONEXEC;
1363898127c3SJohn Johansen 	} else {
1364898127c3SJohn Johansen 		request = AA_MAY_CHANGE_PROFILE;
1365e00b02bbSJohn Johansen 		if (stack)
1366e00b02bbSJohn Johansen 			op = OP_STACK;
1367e00b02bbSJohn Johansen 		else
1368898127c3SJohn Johansen 			op = OP_CHANGE_PROFILE;
1369898127c3SJohn Johansen 	}
1370898127c3SJohn Johansen 
1371e00b02bbSJohn Johansen 	if (*fqname == '&') {
1372e00b02bbSJohn Johansen 		stack = true;
1373e00b02bbSJohn Johansen 		/* don't have label_parse() do stacking */
1374e00b02bbSJohn Johansen 		fqname++;
1375c29bceb3SJohn Johansen 	}
1376e00b02bbSJohn Johansen 	target = aa_label_parse(label, fqname, GFP_KERNEL, true, false);
1377e00b02bbSJohn Johansen 	if (IS_ERR(target)) {
1378e00b02bbSJohn Johansen 		struct aa_profile *tprofile;
1379c29bceb3SJohn Johansen 
1380e00b02bbSJohn Johansen 		info = "label not found";
1381e00b02bbSJohn Johansen 		error = PTR_ERR(target);
1382e00b02bbSJohn Johansen 		target = NULL;
1383e00b02bbSJohn Johansen 		/*
1384e00b02bbSJohn Johansen 		 * TODO: fixme using labels_profile is not right - do profile
1385e00b02bbSJohn Johansen 		 * per complain profile
1386e00b02bbSJohn Johansen 		 */
1387df8073c6SJohn Johansen 		if ((flags & AA_CHANGE_TEST) ||
1388e00b02bbSJohn Johansen 		    !COMPLAIN_MODE(labels_profile(label)))
1389898127c3SJohn Johansen 			goto audit;
1390898127c3SJohn Johansen 		/* released below */
139158f89ce5SJohn Johansen 		tprofile = aa_new_learning_profile(labels_profile(label), false,
1392e00b02bbSJohn Johansen 						   fqname, GFP_KERNEL);
1393e00b02bbSJohn Johansen 		if (!tprofile) {
1394898127c3SJohn Johansen 			info = "failed null profile create";
1395898127c3SJohn Johansen 			error = -ENOMEM;
1396898127c3SJohn Johansen 			goto audit;
1397898127c3SJohn Johansen 		}
1398e00b02bbSJohn Johansen 		target = &tprofile->label;
1399e00b02bbSJohn Johansen 		goto check;
1400898127c3SJohn Johansen 	}
1401898127c3SJohn Johansen 
1402e00b02bbSJohn Johansen 	/*
1403e00b02bbSJohn Johansen 	 * self directed transitions only apply to current policy ns
1404e00b02bbSJohn Johansen 	 * TODO: currently requiring perms for stacking and straight change
1405e00b02bbSJohn Johansen 	 *       stacking doesn't strictly need this. Determine how much
1406e00b02bbSJohn Johansen 	 *       we want to loosen this restriction for stacking
1407e00b02bbSJohn Johansen 	 *
1408e00b02bbSJohn Johansen 	 * if (!stack) {
1409e00b02bbSJohn Johansen 	 */
1410e00b02bbSJohn Johansen 	error = fn_for_each_in_ns(label, profile,
1411e00b02bbSJohn Johansen 			change_profile_perms_wrapper(op, auditname,
1412*690f33e1SJohn Johansen 						     subj_cred,
1413e00b02bbSJohn Johansen 						     profile, target, stack,
1414e00b02bbSJohn Johansen 						     request, &perms));
1415e00b02bbSJohn Johansen 	if (error)
1416e00b02bbSJohn Johansen 		/* auditing done in change_profile_perms_wrapper */
1417e00b02bbSJohn Johansen 		goto out;
1418aa9a39adSJohn Johansen 
1419e00b02bbSJohn Johansen 	/* } */
1420e00b02bbSJohn Johansen 
1421e00b02bbSJohn Johansen check:
1422898127c3SJohn Johansen 	/* check if tracing task is allowed to trace target domain */
1423*690f33e1SJohn Johansen 	error = may_change_ptraced_domain(subj_cred, target, &info);
1424e00b02bbSJohn Johansen 	if (error && !fn_for_each_in_ns(label, profile,
1425e00b02bbSJohn Johansen 					COMPLAIN_MODE(profile)))
1426e00b02bbSJohn Johansen 		goto audit;
1427e00b02bbSJohn Johansen 
1428e00b02bbSJohn Johansen 	/* TODO: add permission check to allow this
1429e00b02bbSJohn Johansen 	 * if ((flags & AA_CHANGE_ONEXEC) && !current_is_single_threaded()) {
1430e00b02bbSJohn Johansen 	 *      info = "not a single threaded task";
1431e00b02bbSJohn Johansen 	 *      error = -EACCES;
1432e00b02bbSJohn Johansen 	 *      goto audit;
1433e00b02bbSJohn Johansen 	 * }
1434e00b02bbSJohn Johansen 	 */
1435e00b02bbSJohn Johansen 	if (flags & AA_CHANGE_TEST)
1436e00b02bbSJohn Johansen 		goto out;
1437e00b02bbSJohn Johansen 
14389fcf78ccSJohn Johansen 	/* stacking is always a subset, so only check the nonstack case */
14399fcf78ccSJohn Johansen 	if (!stack) {
14409fcf78ccSJohn Johansen 		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
14419fcf78ccSJohn Johansen 					   aa_get_label(target),
14429fcf78ccSJohn Johansen 					   aa_get_label(&profile->label));
14439fcf78ccSJohn Johansen 		/*
14449fcf78ccSJohn Johansen 		 * no new privs prevents domain transitions that would
14459fcf78ccSJohn Johansen 		 * reduce restrictions.
14469fcf78ccSJohn Johansen 		 */
14479fcf78ccSJohn Johansen 		if (task_no_new_privs(current) && !unconfined(label) &&
14483ed4aaa9SJohn Johansen 		    !aa_label_is_unconfined_subset(new, ctx->nnp)) {
14499fcf78ccSJohn Johansen 			/* not an apparmor denial per se, so don't log it */
14509fcf78ccSJohn Johansen 			AA_DEBUG("no_new_privs - change_hat denied");
14519fcf78ccSJohn Johansen 			error = -EPERM;
14529fcf78ccSJohn Johansen 			goto out;
14539fcf78ccSJohn Johansen 		}
14549fcf78ccSJohn Johansen 	}
14559fcf78ccSJohn Johansen 
1456e00b02bbSJohn Johansen 	if (!(flags & AA_CHANGE_ONEXEC)) {
1457e00b02bbSJohn Johansen 		/* only transition profiles in the current ns */
1458e00b02bbSJohn Johansen 		if (stack)
1459e00b02bbSJohn Johansen 			new = aa_label_merge(label, target, GFP_KERNEL);
1460e00b02bbSJohn Johansen 		if (IS_ERR_OR_NULL(new)) {
1461e00b02bbSJohn Johansen 			info = "failed to build target label";
1462d6d478aeSJohn Johansen 			if (!new)
1463d6d478aeSJohn Johansen 				error = -ENOMEM;
1464d6d478aeSJohn Johansen 			else
1465e00b02bbSJohn Johansen 				error = PTR_ERR(new);
1466e00b02bbSJohn Johansen 			new = NULL;
1467e00b02bbSJohn Johansen 			perms.allow = 0;
1468898127c3SJohn Johansen 			goto audit;
1469898127c3SJohn Johansen 		}
1470e00b02bbSJohn Johansen 		error = aa_replace_current_label(new);
14719fcf78ccSJohn Johansen 	} else {
14729fcf78ccSJohn Johansen 		if (new) {
14739fcf78ccSJohn Johansen 			aa_put_label(new);
14749fcf78ccSJohn Johansen 			new = NULL;
14759fcf78ccSJohn Johansen 		}
14769fcf78ccSJohn Johansen 
1477e00b02bbSJohn Johansen 		/* full transition will be built in exec path */
1478e00b02bbSJohn Johansen 		error = aa_set_current_onexec(target, stack);
14799fcf78ccSJohn Johansen 	}
1480898127c3SJohn Johansen 
1481898127c3SJohn Johansen audit:
1482e00b02bbSJohn Johansen 	error = fn_for_each_in_ns(label, profile,
1483*690f33e1SJohn Johansen 			aa_audit_file(subj_cred,
1484*690f33e1SJohn Johansen 				      profile, &perms, op, request, auditname,
1485e00b02bbSJohn Johansen 				      NULL, new ? new : target,
1486e00b02bbSJohn Johansen 				      GLOBAL_ROOT_UID, info, error));
1487898127c3SJohn Johansen 
1488e00b02bbSJohn Johansen out:
1489e00b02bbSJohn Johansen 	aa_put_label(new);
1490e00b02bbSJohn Johansen 	aa_put_label(target);
1491637f688dSJohn Johansen 	aa_put_label(label);
1492*690f33e1SJohn Johansen 	put_cred(subj_cred);
1493898127c3SJohn Johansen 
1494898127c3SJohn Johansen 	return error;
1495898127c3SJohn Johansen }
1496