xref: /linux/security/apparmor/domain.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
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
34cd269ca9SYang Li  * @to_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)4390c436a6SJohn Johansen static int may_change_ptraced_domain(const struct cred *to_cred,
4490c436a6SJohn 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;
4990c436a6SJohn Johansen 	const struct cred *tracer_cred = NULL;
5090c436a6SJohn Johansen 
51898127c3SJohn Johansen 	int error = 0;
52898127c3SJohn Johansen 
53898127c3SJohn Johansen 	rcu_read_lock();
5451775fe7SOleg Nesterov 	tracer = ptrace_parent(current);
5590c436a6SJohn Johansen 	if (tracer) {
56898127c3SJohn Johansen 		/* released below */
57637f688dSJohn Johansen 		tracerl = aa_get_task_label(tracer);
5890c436a6SJohn Johansen 		tracer_cred = get_task_cred(tracer);
5990c436a6SJohn Johansen 	}
60898127c3SJohn Johansen 	/* not ptraced */
61637f688dSJohn Johansen 	if (!tracer || unconfined(tracerl))
62898127c3SJohn Johansen 		goto out;
63898127c3SJohn Johansen 
6490c436a6SJohn Johansen 	error = aa_may_ptrace(tracer_cred, tracerl, to_cred, to_label,
6590c436a6SJohn Johansen 			      PTRACE_MODE_ATTACH);
66898127c3SJohn Johansen 
67898127c3SJohn Johansen out:
6804fdc099SJohn Johansen 	rcu_read_unlock();
69637f688dSJohn Johansen 	aa_put_label(tracerl);
7090c436a6SJohn 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
8098b824ffSJohn 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)
9698b824ffSJohn Johansen 		state = aa_dfa_match(rules->file->dfa, state, "&");
9793c98a48SJohn Johansen 	if (profile->ns == tp->ns)
9898b824ffSJohn 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);
10298b824ffSJohn Johansen 	state = aa_dfa_match_len(rules->file->dfa, state, ":", 1);
10398b824ffSJohn Johansen 	state = aa_dfa_match(rules->file->dfa, state, ns_name);
10498b824ffSJohn Johansen 	state = aa_dfa_match_len(rules->file->dfa, state, ":", 1);
10598b824ffSJohn 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;
15398b824ffSJohn 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 	}
15898b824ffSJohn 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:
21398b824ffSJohn 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;
22298b824ffSJohn 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
27576426c9dSGaosheng Cui  * @perms: Returns computed perms (NOT NULL)
276898127c3SJohn Johansen  *
27793c98a48SJohn Johansen  *
278898127c3SJohn Johansen  * Returns: permission set
27993c98a48SJohn Johansen  *
28093c98a48SJohn Johansen  * currently only matches full label A//&B//&C or individual components A, B, C
28193c98a48SJohn Johansen  * not arbitrary combinations. Eg. A//&B, C
282898127c3SJohn Johansen  */
change_profile_perms(struct aa_profile * profile,struct aa_label * target,bool stack,u32 request,aa_state_t start,struct aa_perms * perms)28393c98a48SJohn Johansen static int change_profile_perms(struct aa_profile *profile,
28493c98a48SJohn Johansen 				struct aa_label *target, bool stack,
28533fc95d8SJohn Johansen 				u32 request, aa_state_t start,
28693c98a48SJohn Johansen 				struct aa_perms *perms)
28793c98a48SJohn Johansen {
28893c98a48SJohn Johansen 	if (profile_unconfined(profile)) {
28993c98a48SJohn Johansen 		perms->allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC;
29093c98a48SJohn Johansen 		perms->audit = perms->quiet = perms->kill = 0;
29193c98a48SJohn Johansen 		return 0;
29293c98a48SJohn Johansen 	}
29393c98a48SJohn Johansen 
29493c98a48SJohn Johansen 	/* TODO: add profile in ns screening */
29593c98a48SJohn Johansen 	return label_match(profile, target, stack, start, true, request, perms);
29693c98a48SJohn Johansen }
29793c98a48SJohn Johansen 
298898127c3SJohn Johansen /**
2998e51f908SMatthew Garrett  * aa_xattrs_match - check whether a file matches the xattrs defined in profile
3008e51f908SMatthew Garrett  * @bprm: binprm struct for the process to validate
3018e51f908SMatthew Garrett  * @profile: profile to match against (NOT NULL)
30273f488cdSJohn Johansen  * @state: state to start match in
3038e51f908SMatthew Garrett  *
3048e51f908SMatthew Garrett  * Returns: number of extended attributes that matched, or < 0 on error
3058e51f908SMatthew Garrett  */
aa_xattrs_match(const struct linux_binprm * bprm,struct aa_profile * profile,aa_state_t state)3068e51f908SMatthew Garrett static int aa_xattrs_match(const struct linux_binprm *bprm,
30733fc95d8SJohn Johansen 			   struct aa_profile *profile, aa_state_t state)
3088e51f908SMatthew Garrett {
3098e51f908SMatthew Garrett 	int i;
3108e51f908SMatthew Garrett 	struct dentry *d;
3118e51f908SMatthew Garrett 	char *value = NULL;
312217af7e2SJohn Johansen 	struct aa_attachment *attach = &profile->attach;
31393761c93SLinus Torvalds 	int size, value_size = 0, ret = attach->xattr_count;
3148e51f908SMatthew Garrett 
315217af7e2SJohn Johansen 	if (!bprm || !attach->xattr_count)
3168e51f908SMatthew Garrett 		return 0;
3178c62ed27SJohn Johansen 	might_sleep();
3188e51f908SMatthew Garrett 
31973f488cdSJohn Johansen 	/* transition from exec match to xattr set */
32098b824ffSJohn Johansen 	state = aa_dfa_outofband_transition(attach->xmatch->dfa, state);
3218e51f908SMatthew Garrett 	d = bprm->file->f_path.dentry;
3228e51f908SMatthew Garrett 
323217af7e2SJohn Johansen 	for (i = 0; i < attach->xattr_count; i++) {
3244609e1f1SChristian Brauner 		size = vfs_getxattr_alloc(&nop_mnt_idmap, d, attach->xattrs[i],
325c7c7a1a1STycho Andersen 					  &value, value_size, GFP_KERNEL);
32673f488cdSJohn Johansen 		if (size >= 0) {
3272d63dd43SJohn Johansen 			u32 index, perm;
3288e51f908SMatthew Garrett 
3290df34a64SJohn Johansen 			/*
3300df34a64SJohn Johansen 			 * Check the xattr presence before value. This ensure
3310df34a64SJohn Johansen 			 * that not present xattr can be distinguished from a 0
3320df34a64SJohn Johansen 			 * length value or rule that matches any value
3330df34a64SJohn Johansen 			 */
33498b824ffSJohn Johansen 			state = aa_dfa_null_transition(attach->xmatch->dfa,
335048d4954SJohn Johansen 						       state);
3360df34a64SJohn Johansen 			/* Check xattr value */
33798b824ffSJohn Johansen 			state = aa_dfa_match_len(attach->xmatch->dfa, state,
338048d4954SJohn Johansen 						 value, size);
33998b824ffSJohn Johansen 			index = ACCEPT_TABLE(attach->xmatch->dfa)[state];
34098b824ffSJohn Johansen 			perm = attach->xmatch->perms[index].allow;
34173f488cdSJohn Johansen 			if (!(perm & MAY_EXEC)) {
3428e51f908SMatthew Garrett 				ret = -EINVAL;
3438e51f908SMatthew Garrett 				goto out;
3448e51f908SMatthew Garrett 			}
34573f488cdSJohn Johansen 		}
34673f488cdSJohn Johansen 		/* transition to next element */
34798b824ffSJohn Johansen 		state = aa_dfa_outofband_transition(attach->xmatch->dfa, state);
34873f488cdSJohn Johansen 		if (size < 0) {
34973f488cdSJohn Johansen 			/*
35073f488cdSJohn Johansen 			 * No xattr match, so verify if transition to
35173f488cdSJohn Johansen 			 * next element was valid. IFF so the xattr
35273f488cdSJohn Johansen 			 * was optional.
35373f488cdSJohn Johansen 			 */
35473f488cdSJohn Johansen 			if (!state) {
3558e51f908SMatthew Garrett 				ret = -EINVAL;
3568e51f908SMatthew Garrett 				goto out;
3578e51f908SMatthew Garrett 			}
35873f488cdSJohn Johansen 			/* don't count missing optional xattr as matched */
35973f488cdSJohn Johansen 			ret--;
3608e51f908SMatthew Garrett 		}
3618e51f908SMatthew Garrett 	}
3628e51f908SMatthew Garrett 
3638e51f908SMatthew Garrett out:
3648e51f908SMatthew Garrett 	kfree(value);
3658e51f908SMatthew Garrett 	return ret;
3668e51f908SMatthew Garrett }
3678e51f908SMatthew Garrett 
3688e51f908SMatthew Garrett /**
3698c62ed27SJohn Johansen  * find_attach - do attachment search for unconfined processes
37076426c9dSGaosheng Cui  * @bprm: binprm structure of transitioning task
3718c62ed27SJohn Johansen  * @ns: the current namespace  (NOT NULL)
37276426c9dSGaosheng Cui  * @head: profile list to walk  (NOT NULL)
37376426c9dSGaosheng Cui  * @name: to match against  (NOT NULL)
37476426c9dSGaosheng Cui  * @info: info message if there was an error (NOT NULL)
375898127c3SJohn Johansen  *
376898127c3SJohn Johansen  * Do a linear search on the profiles in the list.  There is a matching
377898127c3SJohn Johansen  * preference where an exact match is preferred over a name which uses
378898127c3SJohn Johansen  * expressions to match, and matching expressions with the greatest
379898127c3SJohn Johansen  * xmatch_len are preferred.
380898127c3SJohn Johansen  *
381898127c3SJohn Johansen  * Requires: @head not be shared or have appropriate locks held
382898127c3SJohn Johansen  *
3838c62ed27SJohn Johansen  * Returns: label or NULL if no match found
384898127c3SJohn Johansen  */
find_attach(const struct linux_binprm * bprm,struct aa_ns * ns,struct list_head * head,const char * name,const char ** info)3858c62ed27SJohn Johansen static struct aa_label *find_attach(const struct linux_binprm *bprm,
3868c62ed27SJohn Johansen 				    struct aa_ns *ns, struct list_head *head,
3878c62ed27SJohn Johansen 				    const char *name, const char **info)
388898127c3SJohn Johansen {
38921f60661SJohn Johansen 	int candidate_len = 0, candidate_xattrs = 0;
390844b8292SJohn Johansen 	bool conflict = false;
391898127c3SJohn Johansen 	struct aa_profile *profile, *candidate = NULL;
392898127c3SJohn Johansen 
39321f60661SJohn Johansen 	AA_BUG(!name);
39421f60661SJohn Johansen 	AA_BUG(!head);
39521f60661SJohn Johansen 
3968c62ed27SJohn Johansen 	rcu_read_lock();
3978c62ed27SJohn Johansen restart:
39801e2b670SJohn Johansen 	list_for_each_entry_rcu(profile, head, base.list) {
399217af7e2SJohn Johansen 		struct aa_attachment *attach = &profile->attach;
400217af7e2SJohn Johansen 
40106d426d1SJohn Johansen 		if (profile->label.flags & FLAG_NULL &&
40206d426d1SJohn Johansen 		    &profile->label == ns_unconfined(profile->ns))
403898127c3SJohn Johansen 			continue;
40406d426d1SJohn Johansen 
4058e51f908SMatthew Garrett 		/* Find the "best" matching profile. Profiles must
4068e51f908SMatthew Garrett 		 * match the path and extended attributes (if any)
4078e51f908SMatthew Garrett 		 * associated with the file. A more specific path
4088e51f908SMatthew Garrett 		 * match will be preferred over a less specific one,
4098e51f908SMatthew Garrett 		 * and a match with more matching extended attributes
4108e51f908SMatthew Garrett 		 * will be preferred over one with fewer. If the best
4118e51f908SMatthew Garrett 		 * match has both the same level of path specificity
4128e51f908SMatthew Garrett 		 * and the same number of matching extended attributes
4138e51f908SMatthew Garrett 		 * as another profile, signal a conflict and refuse to
4148e51f908SMatthew Garrett 		 * match.
4158e51f908SMatthew Garrett 		 */
41698b824ffSJohn Johansen 		if (attach->xmatch->dfa) {
41733fc95d8SJohn Johansen 			unsigned int count;
41833fc95d8SJohn Johansen 			aa_state_t state;
4192d63dd43SJohn Johansen 			u32 index, perm;
420844b8292SJohn Johansen 
42198b824ffSJohn Johansen 			state = aa_dfa_leftmatch(attach->xmatch->dfa,
42298b824ffSJohn Johansen 					attach->xmatch->start[AA_CLASS_XMATCH],
42321f60661SJohn Johansen 					name, &count);
42498b824ffSJohn Johansen 			index = ACCEPT_TABLE(attach->xmatch->dfa)[state];
42598b824ffSJohn Johansen 			perm = attach->xmatch->perms[index].allow;
426898127c3SJohn Johansen 			/* any accepting state means a valid match. */
427898127c3SJohn Johansen 			if (perm & MAY_EXEC) {
4288c62ed27SJohn Johansen 				int ret = 0;
4298e51f908SMatthew Garrett 
43021f60661SJohn Johansen 				if (count < candidate_len)
43121f60661SJohn Johansen 					continue;
43221f60661SJohn Johansen 
433217af7e2SJohn Johansen 				if (bprm && attach->xattr_count) {
4348c62ed27SJohn Johansen 					long rev = READ_ONCE(ns->revision);
4358c62ed27SJohn Johansen 
4368c62ed27SJohn Johansen 					if (!aa_get_profile_not0(profile))
4378c62ed27SJohn Johansen 						goto restart;
4388c62ed27SJohn Johansen 					rcu_read_unlock();
4398c62ed27SJohn Johansen 					ret = aa_xattrs_match(bprm, profile,
4408c62ed27SJohn Johansen 							      state);
4418c62ed27SJohn Johansen 					rcu_read_lock();
4428c62ed27SJohn Johansen 					aa_put_profile(profile);
4438c62ed27SJohn Johansen 					if (rev !=
4448c62ed27SJohn Johansen 					    READ_ONCE(ns->revision))
4458c62ed27SJohn Johansen 						/* policy changed */
4468c62ed27SJohn Johansen 						goto restart;
4478c62ed27SJohn Johansen 					/*
4488c62ed27SJohn Johansen 					 * Fail matching if the xattrs don't
4498c62ed27SJohn Johansen 					 * match
4508c62ed27SJohn Johansen 					 */
4518e51f908SMatthew Garrett 					if (ret < 0)
4528e51f908SMatthew Garrett 						continue;
4538c62ed27SJohn Johansen 				}
45473f488cdSJohn Johansen 				/*
45573f488cdSJohn Johansen 				 * TODO: allow for more flexible best match
45673f488cdSJohn Johansen 				 *
45773f488cdSJohn Johansen 				 * The new match isn't more specific
4588e51f908SMatthew Garrett 				 * than the current best match
4598e51f908SMatthew Garrett 				 */
46021f60661SJohn Johansen 				if (count == candidate_len &&
46121f60661SJohn Johansen 				    ret <= candidate_xattrs) {
4628e51f908SMatthew Garrett 					/* Match is equivalent, so conflict */
46321f60661SJohn Johansen 					if (ret == candidate_xattrs)
4641a3881d3SMatthew Garrett 						conflict = true;
4651a3881d3SMatthew Garrett 					continue;
4661a3881d3SMatthew Garrett 				}
4678e51f908SMatthew Garrett 
4688e51f908SMatthew Garrett 				/* Either the same length with more matching
4698e51f908SMatthew Garrett 				 * xattrs, or a longer match
4708e51f908SMatthew Garrett 				 */
471898127c3SJohn Johansen 				candidate = profile;
472217af7e2SJohn Johansen 				candidate_len = max(count, attach->xmatch_len);
47321f60661SJohn Johansen 				candidate_xattrs = ret;
474844b8292SJohn Johansen 				conflict = false;
475844b8292SJohn Johansen 			}
4768c62ed27SJohn Johansen 		} else if (!strcmp(profile->base.name, name)) {
47773f488cdSJohn Johansen 			/*
47873f488cdSJohn Johansen 			 * old exact non-re match, without conditionals such
47973f488cdSJohn Johansen 			 * as xattrs. no more searching required
48073f488cdSJohn Johansen 			 */
4818c62ed27SJohn Johansen 			candidate = profile;
4828c62ed27SJohn Johansen 			goto out;
4838c62ed27SJohn Johansen 		}
484898127c3SJohn Johansen 	}
485898127c3SJohn Johansen 
4868c62ed27SJohn Johansen 	if (!candidate || conflict) {
4878c62ed27SJohn Johansen 		if (conflict)
488844b8292SJohn Johansen 			*info = "conflicting profile attachments";
4898c62ed27SJohn Johansen 		rcu_read_unlock();
490844b8292SJohn Johansen 		return NULL;
491844b8292SJohn Johansen 	}
492844b8292SJohn Johansen 
4938c62ed27SJohn Johansen out:
4948c62ed27SJohn Johansen 	candidate = aa_get_newest_profile(candidate);
49501e2b670SJohn Johansen 	rcu_read_unlock();
496898127c3SJohn Johansen 
4978c62ed27SJohn Johansen 	return &candidate->label;
498898127c3SJohn Johansen }
499898127c3SJohn Johansen 
next_name(int xtype,const char * name)500898127c3SJohn Johansen static const char *next_name(int xtype, const char *name)
501898127c3SJohn Johansen {
502898127c3SJohn Johansen 	return NULL;
503898127c3SJohn Johansen }
504898127c3SJohn Johansen 
505898127c3SJohn Johansen /**
506898127c3SJohn Johansen  * x_table_lookup - lookup an x transition name via transition table
507898127c3SJohn Johansen  * @profile: current profile (NOT NULL)
508898127c3SJohn Johansen  * @xindex: index into x transition table
50993c98a48SJohn Johansen  * @name: returns: name tested to find label (NOT NULL)
510898127c3SJohn Johansen  *
51193c98a48SJohn Johansen  * Returns: refcounted label, or NULL on failure (MAYBE NULL)
512898127c3SJohn Johansen  */
x_table_lookup(struct aa_profile * profile,u32 xindex,const char ** name)5132ea3ffb7SJohn Johansen struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
51493c98a48SJohn Johansen 				const char **name)
515898127c3SJohn Johansen {
5161ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
5171ad22fccSJohn Johansen 						    typeof(*rules), list);
51893c98a48SJohn Johansen 	struct aa_label *label = NULL;
519898127c3SJohn Johansen 	u32 xtype = xindex & AA_X_TYPE_MASK;
520898127c3SJohn Johansen 	int index = xindex & AA_X_INDEX_MASK;
52193c98a48SJohn Johansen 
52293c98a48SJohn Johansen 	AA_BUG(!name);
523898127c3SJohn Johansen 
524898127c3SJohn Johansen 	/* index is guaranteed to be in range, validated at load time */
52593c98a48SJohn Johansen 	/* TODO: move lookup parsing to unpack time so this is a straight
52693c98a48SJohn Johansen 	 *       index into the resultant label
52793c98a48SJohn Johansen 	 */
52898b824ffSJohn Johansen 	for (*name = rules->file->trans.table[index]; !label && *name;
52993c98a48SJohn Johansen 	     *name = next_name(xtype, *name)) {
530898127c3SJohn Johansen 		if (xindex & AA_X_CHILD) {
53193c98a48SJohn Johansen 			struct aa_profile *new_profile;
532898127c3SJohn Johansen 			/* release by caller */
53393c98a48SJohn Johansen 			new_profile = aa_find_child(profile, *name);
53493c98a48SJohn Johansen 			if (new_profile)
53593c98a48SJohn Johansen 				label = &new_profile->label;
536898127c3SJohn Johansen 			continue;
537898127c3SJohn Johansen 		}
5388ac2ca32SSebastian Andrzej Siewior 		label = aa_label_parse(&profile->label, *name, GFP_KERNEL,
53993c98a48SJohn Johansen 				       true, false);
54093c98a48SJohn Johansen 		if (IS_ERR(label))
54193c98a48SJohn Johansen 			label = NULL;
542898127c3SJohn Johansen 	}
543898127c3SJohn Johansen 
544898127c3SJohn Johansen 	/* released by caller */
545898127c3SJohn Johansen 
54693c98a48SJohn Johansen 	return label;
547898127c3SJohn Johansen }
548898127c3SJohn Johansen 
549898127c3SJohn Johansen /**
55093c98a48SJohn Johansen  * x_to_label - get target label for a given xindex
551898127c3SJohn Johansen  * @profile: current profile  (NOT NULL)
5528e51f908SMatthew Garrett  * @bprm: binprm structure of transitioning task
553898127c3SJohn Johansen  * @name: name to lookup (NOT NULL)
554898127c3SJohn Johansen  * @xindex: index into x transition table
55593c98a48SJohn Johansen  * @lookupname: returns: name used in lookup if one was specified (NOT NULL)
55676426c9dSGaosheng Cui  * @info: info message if there was an error (NOT NULL)
557898127c3SJohn Johansen  *
55893c98a48SJohn Johansen  * find label for a transition index
559898127c3SJohn Johansen  *
56093c98a48SJohn Johansen  * Returns: refcounted label or NULL if not found available
561898127c3SJohn Johansen  */
x_to_label(struct aa_profile * profile,const struct linux_binprm * bprm,const char * name,u32 xindex,const char ** lookupname,const char ** info)56293c98a48SJohn Johansen static struct aa_label *x_to_label(struct aa_profile *profile,
5638e51f908SMatthew Garrett 				   const struct linux_binprm *bprm,
56493c98a48SJohn Johansen 				   const char *name, u32 xindex,
56593c98a48SJohn Johansen 				   const char **lookupname,
56693c98a48SJohn Johansen 				   const char **info)
567898127c3SJohn Johansen {
5681ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
5691ad22fccSJohn Johansen 						    typeof(*rules), list);
57093c98a48SJohn Johansen 	struct aa_label *new = NULL;
57198849dffSJohn Johansen 	struct aa_ns *ns = profile->ns;
572898127c3SJohn Johansen 	u32 xtype = xindex & AA_X_TYPE_MASK;
57393c98a48SJohn Johansen 	const char *stack = NULL;
574898127c3SJohn Johansen 
575898127c3SJohn Johansen 	switch (xtype) {
576898127c3SJohn Johansen 	case AA_X_NONE:
577898127c3SJohn Johansen 		/* fail exec unless ix || ux fallback - handled by caller */
57893c98a48SJohn Johansen 		*lookupname = NULL;
57993c98a48SJohn Johansen 		break;
58093c98a48SJohn Johansen 	case AA_X_TABLE:
58193c98a48SJohn Johansen 		/* TODO: fix when perm mapping done at unload */
58298b824ffSJohn Johansen 		stack = rules->file->trans.table[xindex & AA_X_INDEX_MASK];
58393c98a48SJohn Johansen 		if (*stack != '&') {
58493c98a48SJohn Johansen 			/* released by caller */
58593c98a48SJohn Johansen 			new = x_table_lookup(profile, xindex, lookupname);
58693c98a48SJohn Johansen 			stack = NULL;
58793c98a48SJohn Johansen 			break;
58893c98a48SJohn Johansen 		}
589df561f66SGustavo A. R. Silva 		fallthrough;	/* to X_NAME */
590898127c3SJohn Johansen 	case AA_X_NAME:
591898127c3SJohn Johansen 		if (xindex & AA_X_CHILD)
592898127c3SJohn Johansen 			/* released by caller */
5938e51f908SMatthew Garrett 			new = find_attach(bprm, ns, &profile->base.profiles,
594844b8292SJohn Johansen 					  name, info);
595898127c3SJohn Johansen 		else
596898127c3SJohn Johansen 			/* released by caller */
5978e51f908SMatthew Garrett 			new = find_attach(bprm, ns, &ns->base.profiles,
598844b8292SJohn Johansen 					  name, info);
59993c98a48SJohn Johansen 		*lookupname = name;
600898127c3SJohn Johansen 		break;
601898127c3SJohn Johansen 	}
602898127c3SJohn Johansen 
60393c98a48SJohn Johansen 	if (!new) {
60493c98a48SJohn Johansen 		if (xindex & AA_X_INHERIT) {
60593c98a48SJohn Johansen 			/* (p|c|n)ix - don't change profile but do
60693c98a48SJohn Johansen 			 * use the newest version
60793c98a48SJohn Johansen 			 */
60893c98a48SJohn Johansen 			*info = "ix fallback";
60993c98a48SJohn Johansen 			/* no profile && no error */
61093c98a48SJohn Johansen 			new = aa_get_newest_label(&profile->label);
61193c98a48SJohn Johansen 		} else if (xindex & AA_X_UNCONFINED) {
61293c98a48SJohn Johansen 			new = aa_get_newest_label(ns_unconfined(profile->ns));
61393c98a48SJohn Johansen 			*info = "ux fallback";
61493c98a48SJohn Johansen 		}
61593c98a48SJohn Johansen 	}
61693c98a48SJohn Johansen 
61793c98a48SJohn Johansen 	if (new && stack) {
61893c98a48SJohn Johansen 		/* base the stack on post domain transition */
61993c98a48SJohn Johansen 		struct aa_label *base = new;
62093c98a48SJohn Johansen 
6218ac2ca32SSebastian Andrzej Siewior 		new = aa_label_parse(base, stack, GFP_KERNEL, true, false);
62293c98a48SJohn Johansen 		if (IS_ERR(new))
62393c98a48SJohn Johansen 			new = NULL;
62493c98a48SJohn Johansen 		aa_put_label(base);
62593c98a48SJohn Johansen 	}
62693c98a48SJohn Johansen 
627898127c3SJohn Johansen 	/* released by caller */
62893c98a48SJohn Johansen 	return new;
62993c98a48SJohn Johansen }
63093c98a48SJohn 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)63190c436a6SJohn Johansen static struct aa_label *profile_transition(const struct cred *subj_cred,
63290c436a6SJohn Johansen 					   struct aa_profile *profile,
63393c98a48SJohn Johansen 					   const struct linux_binprm *bprm,
63493c98a48SJohn Johansen 					   char *buffer, struct path_cond *cond,
63593c98a48SJohn Johansen 					   bool *secure_exec)
63693c98a48SJohn Johansen {
6371ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
6381ad22fccSJohn Johansen 						    typeof(*rules), list);
63993c98a48SJohn Johansen 	struct aa_label *new = NULL;
64093c98a48SJohn Johansen 	const char *info = NULL, *name = NULL, *target = NULL;
64198b824ffSJohn Johansen 	aa_state_t state = rules->file->start[AA_CLASS_FILE];
64293c98a48SJohn Johansen 	struct aa_perms perms = {};
64393c98a48SJohn Johansen 	bool nonewprivs = false;
64493c98a48SJohn Johansen 	int error = 0;
64593c98a48SJohn Johansen 
64693c98a48SJohn Johansen 	AA_BUG(!profile);
64793c98a48SJohn Johansen 	AA_BUG(!bprm);
64893c98a48SJohn Johansen 	AA_BUG(!buffer);
64993c98a48SJohn Johansen 
65093c98a48SJohn Johansen 	error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
65193c98a48SJohn Johansen 			     &name, &info, profile->disconnected);
65293c98a48SJohn Johansen 	if (error) {
65393c98a48SJohn Johansen 		if (profile_unconfined(profile) ||
65493c98a48SJohn Johansen 		    (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
65593c98a48SJohn Johansen 			AA_DEBUG("name lookup ix on error");
65693c98a48SJohn Johansen 			error = 0;
65793c98a48SJohn Johansen 			new = aa_get_newest_label(&profile->label);
65893c98a48SJohn Johansen 		}
65993c98a48SJohn Johansen 		name = bprm->filename;
66093c98a48SJohn Johansen 		goto audit;
66193c98a48SJohn Johansen 	}
66293c98a48SJohn Johansen 
66393c98a48SJohn Johansen 	if (profile_unconfined(profile)) {
6648e51f908SMatthew Garrett 		new = find_attach(bprm, profile->ns,
6658e51f908SMatthew Garrett 				  &profile->ns->base.profiles, name, &info);
66693c98a48SJohn Johansen 		if (new) {
66793c98a48SJohn Johansen 			AA_DEBUG("unconfined attached to new label");
66893c98a48SJohn Johansen 			return new;
66993c98a48SJohn Johansen 		}
67093c98a48SJohn Johansen 		AA_DEBUG("unconfined exec no attachment");
67193c98a48SJohn Johansen 		return aa_get_newest_label(&profile->label);
67293c98a48SJohn Johansen 	}
67393c98a48SJohn Johansen 
67493c98a48SJohn Johansen 	/* find exec permissions for name */
67598b824ffSJohn Johansen 	state = aa_str_perms(rules->file, state, name, cond, &perms);
67693c98a48SJohn Johansen 	if (perms.allow & MAY_EXEC) {
67793c98a48SJohn Johansen 		/* exec permission determine how to transition */
6788e51f908SMatthew Garrett 		new = x_to_label(profile, bprm, name, perms.xindex, &target,
6798e51f908SMatthew Garrett 				 &info);
68093c98a48SJohn Johansen 		if (new && new->proxy == profile->label.proxy && info) {
68193c98a48SJohn Johansen 			/* hack ix fallback - improve how this is detected */
68293c98a48SJohn Johansen 			goto audit;
68393c98a48SJohn Johansen 		} else if (!new) {
68493c98a48SJohn Johansen 			error = -EACCES;
68593c98a48SJohn Johansen 			info = "profile transition not found";
68693c98a48SJohn Johansen 			/* remove MAY_EXEC to audit as failure */
68793c98a48SJohn Johansen 			perms.allow &= ~MAY_EXEC;
68893c98a48SJohn Johansen 		}
68993c98a48SJohn Johansen 	} else if (COMPLAIN_MODE(profile)) {
69093c98a48SJohn Johansen 		/* no exec permission - learning mode */
6915d7c44efSJohn Johansen 		struct aa_profile *new_profile = NULL;
6925d7c44efSJohn Johansen 
69358f89ce5SJohn Johansen 		new_profile = aa_new_learning_profile(profile, false, name,
6945d7c44efSJohn Johansen 						      GFP_KERNEL);
69593c98a48SJohn Johansen 		if (!new_profile) {
69693c98a48SJohn Johansen 			error = -ENOMEM;
69793c98a48SJohn Johansen 			info = "could not create null profile";
69893c98a48SJohn Johansen 		} else {
69993c98a48SJohn Johansen 			error = -EACCES;
70093c98a48SJohn Johansen 			new = &new_profile->label;
70193c98a48SJohn Johansen 		}
70293c98a48SJohn Johansen 		perms.xindex |= AA_X_UNSAFE;
70393c98a48SJohn Johansen 	} else
70493c98a48SJohn Johansen 		/* fail exec */
70593c98a48SJohn Johansen 		error = -EACCES;
70693c98a48SJohn Johansen 
70793c98a48SJohn Johansen 	if (!new)
70893c98a48SJohn Johansen 		goto audit;
70993c98a48SJohn Johansen 
71093c98a48SJohn Johansen 
71193c98a48SJohn Johansen 	if (!(perms.xindex & AA_X_UNSAFE)) {
71293c98a48SJohn Johansen 		if (DEBUG_ON) {
71393c98a48SJohn Johansen 			dbg_printk("apparmor: scrubbing environment variables"
71493c98a48SJohn Johansen 				   " for %s profile=", name);
7158ac2ca32SSebastian Andrzej Siewior 			aa_label_printk(new, GFP_KERNEL);
71693c98a48SJohn Johansen 			dbg_printk("\n");
71793c98a48SJohn Johansen 		}
71893c98a48SJohn Johansen 		*secure_exec = true;
71993c98a48SJohn Johansen 	}
72093c98a48SJohn Johansen 
72193c98a48SJohn Johansen audit:
72290c436a6SJohn Johansen 	aa_audit_file(subj_cred, profile, &perms, OP_EXEC, MAY_EXEC, name,
72390c436a6SJohn Johansen 		      target, new,
72493c98a48SJohn Johansen 		      cond->uid, info, error);
72593c98a48SJohn Johansen 	if (!new || nonewprivs) {
72693c98a48SJohn Johansen 		aa_put_label(new);
72793c98a48SJohn Johansen 		return ERR_PTR(error);
72893c98a48SJohn Johansen 	}
72993c98a48SJohn Johansen 
73093c98a48SJohn Johansen 	return new;
73193c98a48SJohn Johansen }
73293c98a48SJohn 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)73390c436a6SJohn Johansen static int profile_onexec(const struct cred *subj_cred,
73490c436a6SJohn Johansen 			  struct aa_profile *profile, struct aa_label *onexec,
73593c98a48SJohn Johansen 			  bool stack, const struct linux_binprm *bprm,
73693c98a48SJohn Johansen 			  char *buffer, struct path_cond *cond,
73793c98a48SJohn Johansen 			  bool *secure_exec)
73893c98a48SJohn Johansen {
7391ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
7401ad22fccSJohn Johansen 						    typeof(*rules), list);
74198b824ffSJohn Johansen 	aa_state_t state = rules->file->start[AA_CLASS_FILE];
74293c98a48SJohn Johansen 	struct aa_perms perms = {};
74393c98a48SJohn Johansen 	const char *xname = NULL, *info = "change_profile onexec";
74493c98a48SJohn Johansen 	int error = -EACCES;
74593c98a48SJohn Johansen 
74693c98a48SJohn Johansen 	AA_BUG(!profile);
74793c98a48SJohn Johansen 	AA_BUG(!onexec);
74893c98a48SJohn Johansen 	AA_BUG(!bprm);
74993c98a48SJohn Johansen 	AA_BUG(!buffer);
75093c98a48SJohn Johansen 
75193c98a48SJohn Johansen 	if (profile_unconfined(profile)) {
75293c98a48SJohn Johansen 		/* change_profile on exec already granted */
75393c98a48SJohn Johansen 		/*
75493c98a48SJohn Johansen 		 * NOTE: Domain transitions from unconfined are allowed
75593c98a48SJohn Johansen 		 * even when no_new_privs is set because this aways results
75693c98a48SJohn Johansen 		 * in a further reduction of permissions.
75793c98a48SJohn Johansen 		 */
75893c98a48SJohn Johansen 		return 0;
75993c98a48SJohn Johansen 	}
76093c98a48SJohn Johansen 
76193c98a48SJohn Johansen 	error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
76293c98a48SJohn Johansen 			     &xname, &info, profile->disconnected);
76393c98a48SJohn Johansen 	if (error) {
76493c98a48SJohn Johansen 		if (profile_unconfined(profile) ||
76593c98a48SJohn Johansen 		    (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
76693c98a48SJohn Johansen 			AA_DEBUG("name lookup ix on error");
76793c98a48SJohn Johansen 			error = 0;
76893c98a48SJohn Johansen 		}
76993c98a48SJohn Johansen 		xname = bprm->filename;
77093c98a48SJohn Johansen 		goto audit;
77193c98a48SJohn Johansen 	}
77293c98a48SJohn Johansen 
77393c98a48SJohn Johansen 	/* find exec permissions for name */
77498b824ffSJohn Johansen 	state = aa_str_perms(rules->file, state, xname, cond, &perms);
77593c98a48SJohn Johansen 	if (!(perms.allow & AA_MAY_ONEXEC)) {
77693c98a48SJohn Johansen 		info = "no change_onexec valid for executable";
77793c98a48SJohn Johansen 		goto audit;
77893c98a48SJohn Johansen 	}
77993c98a48SJohn Johansen 	/* test if this exec can be paired with change_profile onexec.
78093c98a48SJohn Johansen 	 * onexec permission is linked to exec with a standard pairing
78193c98a48SJohn Johansen 	 * exec\0change_profile
78293c98a48SJohn Johansen 	 */
78398b824ffSJohn Johansen 	state = aa_dfa_null_transition(rules->file->dfa, state);
78493c98a48SJohn Johansen 	error = change_profile_perms(profile, onexec, stack, AA_MAY_ONEXEC,
78593c98a48SJohn Johansen 				     state, &perms);
78693c98a48SJohn Johansen 	if (error) {
78793c98a48SJohn Johansen 		perms.allow &= ~AA_MAY_ONEXEC;
78893c98a48SJohn Johansen 		goto audit;
78993c98a48SJohn Johansen 	}
79093c98a48SJohn Johansen 
79193c98a48SJohn Johansen 	if (!(perms.xindex & AA_X_UNSAFE)) {
79293c98a48SJohn Johansen 		if (DEBUG_ON) {
79393c98a48SJohn Johansen 			dbg_printk("apparmor: scrubbing environment "
79493c98a48SJohn Johansen 				   "variables for %s label=", xname);
7958ac2ca32SSebastian Andrzej Siewior 			aa_label_printk(onexec, GFP_KERNEL);
79693c98a48SJohn Johansen 			dbg_printk("\n");
79793c98a48SJohn Johansen 		}
79893c98a48SJohn Johansen 		*secure_exec = true;
79993c98a48SJohn Johansen 	}
80093c98a48SJohn Johansen 
80193c98a48SJohn Johansen audit:
80290c436a6SJohn Johansen 	return aa_audit_file(subj_cred, profile, &perms, OP_EXEC,
80390c436a6SJohn Johansen 			     AA_MAY_ONEXEC, xname,
80493c98a48SJohn Johansen 			     NULL, onexec, cond->uid, info, error);
80593c98a48SJohn Johansen }
80693c98a48SJohn Johansen 
80793c98a48SJohn Johansen /* ensure none ns domain transitions are correctly applied with onexec */
80893c98a48SJohn 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)80990c436a6SJohn Johansen static struct aa_label *handle_onexec(const struct cred *subj_cred,
81090c436a6SJohn Johansen 				      struct aa_label *label,
81193c98a48SJohn Johansen 				      struct aa_label *onexec, bool stack,
81293c98a48SJohn Johansen 				      const struct linux_binprm *bprm,
81393c98a48SJohn Johansen 				      char *buffer, struct path_cond *cond,
81493c98a48SJohn Johansen 				      bool *unsafe)
81593c98a48SJohn Johansen {
81693c98a48SJohn Johansen 	struct aa_profile *profile;
81793c98a48SJohn Johansen 	struct aa_label *new;
81893c98a48SJohn Johansen 	int error;
81993c98a48SJohn Johansen 
82093c98a48SJohn Johansen 	AA_BUG(!label);
82193c98a48SJohn Johansen 	AA_BUG(!onexec);
82293c98a48SJohn Johansen 	AA_BUG(!bprm);
82393c98a48SJohn Johansen 	AA_BUG(!buffer);
82493c98a48SJohn Johansen 
82593c98a48SJohn Johansen 	if (!stack) {
82693c98a48SJohn Johansen 		error = fn_for_each_in_ns(label, profile,
82790c436a6SJohn Johansen 				profile_onexec(subj_cred, profile, onexec, stack,
82893c98a48SJohn Johansen 					       bprm, buffer, cond, unsafe));
82993c98a48SJohn Johansen 		if (error)
83093c98a48SJohn Johansen 			return ERR_PTR(error);
8318ac2ca32SSebastian Andrzej Siewior 		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
83293c98a48SJohn Johansen 				aa_get_newest_label(onexec),
83390c436a6SJohn Johansen 				profile_transition(subj_cred, profile, bprm,
83490c436a6SJohn Johansen 						   buffer,
83593c98a48SJohn Johansen 						   cond, unsafe));
83693c98a48SJohn Johansen 
83793c98a48SJohn Johansen 	} else {
838b2c2086cSZygmunt Krynicki 		/* TODO: determine how much we want to loosen this */
83993c98a48SJohn Johansen 		error = fn_for_each_in_ns(label, profile,
84090c436a6SJohn Johansen 				profile_onexec(subj_cred, profile, onexec, stack, bprm,
84193c98a48SJohn Johansen 					       buffer, cond, unsafe));
84293c98a48SJohn Johansen 		if (error)
84393c98a48SJohn Johansen 			return ERR_PTR(error);
8448ac2ca32SSebastian Andrzej Siewior 		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
84593c98a48SJohn Johansen 				aa_label_merge(&profile->label, onexec,
8468ac2ca32SSebastian Andrzej Siewior 					       GFP_KERNEL),
84790c436a6SJohn Johansen 				profile_transition(subj_cred, profile, bprm,
84890c436a6SJohn Johansen 						   buffer,
84993c98a48SJohn Johansen 						   cond, unsafe));
85093c98a48SJohn Johansen 	}
85193c98a48SJohn Johansen 
85293c98a48SJohn Johansen 	if (new)
85393c98a48SJohn Johansen 		return new;
85493c98a48SJohn Johansen 
85593c98a48SJohn Johansen 	/* TODO: get rid of GLOBAL_ROOT_UID */
85693c98a48SJohn Johansen 	error = fn_for_each_in_ns(label, profile,
85790c436a6SJohn Johansen 			aa_audit_file(subj_cred, profile, &nullperms,
85890c436a6SJohn Johansen 				      OP_CHANGE_ONEXEC,
85993c98a48SJohn Johansen 				      AA_MAY_ONEXEC, bprm->filename, NULL,
86093c98a48SJohn Johansen 				      onexec, GLOBAL_ROOT_UID,
86193c98a48SJohn Johansen 				      "failed to build target label", -ENOMEM));
86293c98a48SJohn Johansen 	return ERR_PTR(error);
863898127c3SJohn Johansen }
864898127c3SJohn Johansen 
865898127c3SJohn Johansen /**
866b8bff599SEric W. Biederman  * apparmor_bprm_creds_for_exec - Update the new creds on the bprm struct
867898127c3SJohn Johansen  * @bprm: binprm for the exec  (NOT NULL)
868898127c3SJohn Johansen  *
869898127c3SJohn Johansen  * Returns: %0 or error on failure
87093c98a48SJohn Johansen  *
87193c98a48SJohn Johansen  * TODO: once the other paths are done see if we can't refactor into a fn
872898127c3SJohn Johansen  */
apparmor_bprm_creds_for_exec(struct linux_binprm * bprm)873b8bff599SEric W. Biederman int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
874898127c3SJohn Johansen {
875f175221aSJohn Johansen 	struct aa_task_ctx *ctx;
87693c98a48SJohn Johansen 	struct aa_label *label, *new = NULL;
87790c436a6SJohn Johansen 	const struct cred *subj_cred;
87893c98a48SJohn Johansen 	struct aa_profile *profile;
879898127c3SJohn Johansen 	char *buffer = NULL;
88093c98a48SJohn Johansen 	const char *info = NULL;
88193c98a48SJohn Johansen 	int error = 0;
88293c98a48SJohn Johansen 	bool unsafe = false;
883e67fe633SChristian Brauner 	vfsuid_t vfsuid = i_uid_into_vfsuid(file_mnt_idmap(bprm->file),
8843cee6079SChristian Brauner 					    file_inode(bprm->file));
885898127c3SJohn Johansen 	struct path_cond cond = {
8865e26a01eSChristian Brauner 		vfsuid_into_kuid(vfsuid),
887496ad9aaSAl Viro 		file_inode(bprm->file)->i_mode
888898127c3SJohn Johansen 	};
889898127c3SJohn Johansen 
89090c436a6SJohn Johansen 	subj_cred = current_cred();
891de62de59SJohn Johansen 	ctx = task_ctx(current);
892d9087c49SJohn Johansen 	AA_BUG(!cred_label(bprm->cred));
893f175221aSJohn Johansen 	AA_BUG(!ctx);
894898127c3SJohn Johansen 
895d9087c49SJohn Johansen 	label = aa_get_newest_label(cred_label(bprm->cred));
8964227c333SJohn Johansen 
8979fcf78ccSJohn Johansen 	/*
8989fcf78ccSJohn Johansen 	 * Detect no new privs being set, and store the label it
8999fcf78ccSJohn Johansen 	 * occurred under. Ideally this would happen when nnp
9009fcf78ccSJohn Johansen 	 * is set but there isn't a good way to do that yet.
9019fcf78ccSJohn Johansen 	 *
9029fcf78ccSJohn Johansen 	 * Testing for unconfined must be done before the subset test
9039fcf78ccSJohn Johansen 	 */
9049fcf78ccSJohn Johansen 	if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && !unconfined(label) &&
9059fcf78ccSJohn Johansen 	    !ctx->nnp)
9069fcf78ccSJohn Johansen 		ctx->nnp = aa_get_label(label);
9079fcf78ccSJohn Johansen 
9084227c333SJohn Johansen 	/* buffer freed below, name is pointer into buffer */
909341c1fdaSJohn Johansen 	buffer = aa_get_buffer(false);
910df323337SSebastian Andrzej Siewior 	if (!buffer) {
911df323337SSebastian Andrzej Siewior 		error = -ENOMEM;
912df323337SSebastian Andrzej Siewior 		goto done;
913df323337SSebastian Andrzej Siewior 	}
914df323337SSebastian Andrzej Siewior 
91593c98a48SJohn Johansen 	/* Test for onexec first as onexec override other x transitions. */
916f175221aSJohn Johansen 	if (ctx->onexec)
91790c436a6SJohn Johansen 		new = handle_onexec(subj_cred, label, ctx->onexec, ctx->token,
91893c98a48SJohn Johansen 				    bprm, buffer, &cond, &unsafe);
919898127c3SJohn Johansen 	else
9208ac2ca32SSebastian Andrzej Siewior 		new = fn_label_build(label, profile, GFP_KERNEL,
92190c436a6SJohn Johansen 				profile_transition(subj_cred, profile, bprm,
92290c436a6SJohn Johansen 						   buffer,
92393c98a48SJohn Johansen 						   &cond, &unsafe));
924898127c3SJohn Johansen 
92593c98a48SJohn Johansen 	AA_BUG(!new);
92693c98a48SJohn Johansen 	if (IS_ERR(new)) {
92793c98a48SJohn Johansen 		error = PTR_ERR(new);
92893c98a48SJohn Johansen 		goto done;
92993c98a48SJohn Johansen 	} else if (!new) {
930898127c3SJohn Johansen 		error = -ENOMEM;
93193c98a48SJohn Johansen 		goto done;
932c29bceb3SJohn Johansen 	}
933c29bceb3SJohn Johansen 
9349fcf78ccSJohn Johansen 	/* Policy has specified a domain transitions. If no_new_privs and
9359fcf78ccSJohn Johansen 	 * confined ensure the transition is to confinement that is subset
9369fcf78ccSJohn Johansen 	 * of the confinement when the task entered no new privs.
9379fcf78ccSJohn Johansen 	 *
9389fcf78ccSJohn Johansen 	 * NOTE: Domain transitions from unconfined and to stacked
9399fcf78ccSJohn Johansen 	 * subsets are allowed even when no_new_privs is set because this
9409fcf78ccSJohn Johansen 	 * aways results in a further reduction of permissions.
9419fcf78ccSJohn Johansen 	 */
9429fcf78ccSJohn Johansen 	if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) &&
9433ed4aaa9SJohn Johansen 	    !unconfined(label) &&
9443ed4aaa9SJohn Johansen 	    !aa_label_is_unconfined_subset(new, ctx->nnp)) {
9459fcf78ccSJohn Johansen 		error = -EPERM;
9469fcf78ccSJohn Johansen 		info = "no new privs";
9479fcf78ccSJohn Johansen 		goto audit;
9489fcf78ccSJohn Johansen 	}
949898127c3SJohn Johansen 
950898127c3SJohn Johansen 	if (bprm->unsafe & LSM_UNSAFE_SHARE) {
951898127c3SJohn Johansen 		/* FIXME: currently don't mediate shared state */
952898127c3SJohn Johansen 		;
953898127c3SJohn Johansen 	}
954898127c3SJohn Johansen 
95593c98a48SJohn Johansen 	if (bprm->unsafe & (LSM_UNSAFE_PTRACE)) {
95693c98a48SJohn Johansen 		/* TODO: test needs to be profile of label to new */
95790c436a6SJohn Johansen 		error = may_change_ptraced_domain(bprm->cred, new, &info);
958f7da2de0SJohn Johansen 		if (error)
959898127c3SJohn Johansen 			goto audit;
960898127c3SJohn Johansen 	}
961898127c3SJohn Johansen 
96293c98a48SJohn Johansen 	if (unsafe) {
96393c98a48SJohn Johansen 		if (DEBUG_ON) {
96493c98a48SJohn Johansen 			dbg_printk("scrubbing environment variables for %s "
96593c98a48SJohn Johansen 				   "label=", bprm->filename);
9668ac2ca32SSebastian Andrzej Siewior 			aa_label_printk(new, GFP_KERNEL);
96793c98a48SJohn Johansen 			dbg_printk("\n");
96893c98a48SJohn Johansen 		}
969993b3ab0SKees Cook 		bprm->secureexec = 1;
970898127c3SJohn Johansen 	}
97193c98a48SJohn Johansen 
97293c98a48SJohn Johansen 	if (label->proxy != new->proxy) {
97393c98a48SJohn Johansen 		/* when transitioning clear unsafe personality bits */
97493c98a48SJohn Johansen 		if (DEBUG_ON) {
97593c98a48SJohn Johansen 			dbg_printk("apparmor: clearing unsafe personality "
97693c98a48SJohn Johansen 				   "bits. %s label=", bprm->filename);
9778ac2ca32SSebastian Andrzej Siewior 			aa_label_printk(new, GFP_KERNEL);
97893c98a48SJohn Johansen 			dbg_printk("\n");
97993c98a48SJohn Johansen 		}
980898127c3SJohn Johansen 		bprm->per_clear |= PER_CLEAR_ON_SETID;
98193c98a48SJohn Johansen 	}
982d9087c49SJohn Johansen 	aa_put_label(cred_label(bprm->cred));
983d9087c49SJohn Johansen 	/* transfer reference, released when cred is freed */
98469b5a44aSCasey Schaufler 	set_cred_label(bprm->cred, new);
985898127c3SJohn Johansen 
98693c98a48SJohn Johansen done:
987637f688dSJohn Johansen 	aa_put_label(label);
988df323337SSebastian Andrzej Siewior 	aa_put_buffer(buffer);
989898127c3SJohn Johansen 
990898127c3SJohn Johansen 	return error;
99193c98a48SJohn Johansen 
99293c98a48SJohn Johansen audit:
99393c98a48SJohn Johansen 	error = fn_for_each(label, profile,
99490c436a6SJohn Johansen 			aa_audit_file(current_cred(), profile, &nullperms,
99590c436a6SJohn Johansen 				      OP_EXEC, MAY_EXEC,
99693c98a48SJohn Johansen 				      bprm->filename, NULL, new,
9975e26a01eSChristian Brauner 				      vfsuid_into_kuid(vfsuid), info, error));
99893c98a48SJohn Johansen 	aa_put_label(new);
99993c98a48SJohn Johansen 	goto done;
1000898127c3SJohn Johansen }
1001898127c3SJohn Johansen 
1002898127c3SJohn Johansen /*
1003898127c3SJohn Johansen  * Functions for self directed profile change
1004898127c3SJohn Johansen  */
1005898127c3SJohn Johansen 
100689dbf196SJohn Johansen 
100789dbf196SJohn Johansen /* helper fn for change_hat
1008898127c3SJohn Johansen  *
100989dbf196SJohn Johansen  * Returns: label for hat transition OR ERR_PTR.  Does NOT return NULL
1010898127c3SJohn Johansen  */
build_change_hat(const struct cred * subj_cred,struct aa_profile * profile,const char * name,bool sibling)101190c436a6SJohn Johansen static struct aa_label *build_change_hat(const struct cred *subj_cred,
101290c436a6SJohn Johansen 					 struct aa_profile *profile,
101389dbf196SJohn Johansen 					 const char *name, bool sibling)
1014898127c3SJohn Johansen {
101589dbf196SJohn Johansen 	struct aa_profile *root, *hat = NULL;
101689dbf196SJohn Johansen 	const char *info = NULL;
101789dbf196SJohn Johansen 	int error = 0;
101889dbf196SJohn Johansen 
101989dbf196SJohn Johansen 	if (sibling && PROFILE_IS_HAT(profile)) {
102089dbf196SJohn Johansen 		root = aa_get_profile_rcu(&profile->parent);
102189dbf196SJohn Johansen 	} else if (!sibling && !PROFILE_IS_HAT(profile)) {
102289dbf196SJohn Johansen 		root = aa_get_profile(profile);
102389dbf196SJohn Johansen 	} else {
102489dbf196SJohn Johansen 		info = "conflicting target types";
102589dbf196SJohn Johansen 		error = -EPERM;
102689dbf196SJohn Johansen 		goto audit;
102789dbf196SJohn Johansen 	}
102889dbf196SJohn Johansen 
102989dbf196SJohn Johansen 	hat = aa_find_child(root, name);
103089dbf196SJohn Johansen 	if (!hat) {
103189dbf196SJohn Johansen 		error = -ENOENT;
103289dbf196SJohn Johansen 		if (COMPLAIN_MODE(profile)) {
103358f89ce5SJohn Johansen 			hat = aa_new_learning_profile(profile, true, name,
103489dbf196SJohn Johansen 						      GFP_KERNEL);
103589dbf196SJohn Johansen 			if (!hat) {
103689dbf196SJohn Johansen 				info = "failed null profile create";
103789dbf196SJohn Johansen 				error = -ENOMEM;
103889dbf196SJohn Johansen 			}
103989dbf196SJohn Johansen 		}
104089dbf196SJohn Johansen 	}
104189dbf196SJohn Johansen 	aa_put_profile(root);
104289dbf196SJohn Johansen 
104389dbf196SJohn Johansen audit:
104490c436a6SJohn Johansen 	aa_audit_file(subj_cred, profile, &nullperms, OP_CHANGE_HAT,
104590c436a6SJohn Johansen 		      AA_MAY_CHANGEHAT,
104689dbf196SJohn Johansen 		      name, hat ? hat->base.hname : NULL,
104724b87a16SJohn Johansen 		      hat ? &hat->label : NULL, GLOBAL_ROOT_UID, info,
104889dbf196SJohn Johansen 		      error);
104989dbf196SJohn Johansen 	if (!hat || (error && error != -ENOENT))
105089dbf196SJohn Johansen 		return ERR_PTR(error);
105189dbf196SJohn Johansen 	/* if hat && error - complain mode, already audited and we adjust for
105289dbf196SJohn Johansen 	 * complain mode allow by returning hat->label
105389dbf196SJohn Johansen 	 */
105489dbf196SJohn Johansen 	return &hat->label;
105589dbf196SJohn Johansen }
105689dbf196SJohn Johansen 
105789dbf196SJohn Johansen /* helper fn for changing into a hat
105889dbf196SJohn Johansen  *
105989dbf196SJohn Johansen  * Returns: label for hat transition or ERR_PTR. Does not return NULL
106089dbf196SJohn Johansen  */
change_hat(const struct cred * subj_cred,struct aa_label * label,const char * hats[],int count,int flags)106190c436a6SJohn Johansen static struct aa_label *change_hat(const struct cred *subj_cred,
106290c436a6SJohn Johansen 				   struct aa_label *label, const char *hats[],
106389dbf196SJohn Johansen 				   int count, int flags)
106489dbf196SJohn Johansen {
106589dbf196SJohn Johansen 	struct aa_profile *profile, *root, *hat = NULL;
106689dbf196SJohn Johansen 	struct aa_label *new;
106789dbf196SJohn Johansen 	struct label_it it;
106889dbf196SJohn Johansen 	bool sibling = false;
106989dbf196SJohn Johansen 	const char *name, *info = NULL;
107089dbf196SJohn Johansen 	int i, error;
107189dbf196SJohn Johansen 
107289dbf196SJohn Johansen 	AA_BUG(!label);
107389dbf196SJohn Johansen 	AA_BUG(!hats);
107489dbf196SJohn Johansen 	AA_BUG(count < 1);
107589dbf196SJohn Johansen 
107689dbf196SJohn Johansen 	if (PROFILE_IS_HAT(labels_profile(label)))
107789dbf196SJohn Johansen 		sibling = true;
107889dbf196SJohn Johansen 
107989dbf196SJohn Johansen 	/*find first matching hat */
108089dbf196SJohn Johansen 	for (i = 0; i < count && !hat; i++) {
108189dbf196SJohn Johansen 		name = hats[i];
108289dbf196SJohn Johansen 		label_for_each_in_ns(it, labels_ns(label), label, profile) {
108389dbf196SJohn Johansen 			if (sibling && PROFILE_IS_HAT(profile)) {
108489dbf196SJohn Johansen 				root = aa_get_profile_rcu(&profile->parent);
108589dbf196SJohn Johansen 			} else if (!sibling && !PROFILE_IS_HAT(profile)) {
108689dbf196SJohn Johansen 				root = aa_get_profile(profile);
108789dbf196SJohn Johansen 			} else {	/* conflicting change type */
108889dbf196SJohn Johansen 				info = "conflicting targets types";
108989dbf196SJohn Johansen 				error = -EPERM;
109089dbf196SJohn Johansen 				goto fail;
109189dbf196SJohn Johansen 			}
109289dbf196SJohn Johansen 			hat = aa_find_child(root, name);
109389dbf196SJohn Johansen 			aa_put_profile(root);
109489dbf196SJohn Johansen 			if (!hat) {
109589dbf196SJohn Johansen 				if (!COMPLAIN_MODE(profile))
109689dbf196SJohn Johansen 					goto outer_continue;
109789dbf196SJohn Johansen 				/* complain mode succeed as if hat */
109889dbf196SJohn Johansen 			} else if (!PROFILE_IS_HAT(hat)) {
109989dbf196SJohn Johansen 				info = "target not hat";
110089dbf196SJohn Johansen 				error = -EPERM;
110189dbf196SJohn Johansen 				aa_put_profile(hat);
110289dbf196SJohn Johansen 				goto fail;
110389dbf196SJohn Johansen 			}
110489dbf196SJohn Johansen 			aa_put_profile(hat);
110589dbf196SJohn Johansen 		}
110689dbf196SJohn Johansen 		/* found a hat for all profiles in ns */
110789dbf196SJohn Johansen 		goto build;
110889dbf196SJohn Johansen outer_continue:
110989dbf196SJohn Johansen 	;
111089dbf196SJohn Johansen 	}
111189dbf196SJohn Johansen 	/* no hats that match, find appropriate error
111289dbf196SJohn Johansen 	 *
111389dbf196SJohn Johansen 	 * In complain mode audit of the failure is based off of the first
111489dbf196SJohn Johansen 	 * hat supplied.  This is done due how userspace interacts with
111589dbf196SJohn Johansen 	 * change_hat.
111689dbf196SJohn Johansen 	 */
111789dbf196SJohn Johansen 	name = NULL;
111889dbf196SJohn Johansen 	label_for_each_in_ns(it, labels_ns(label), label, profile) {
111989dbf196SJohn Johansen 		if (!list_empty(&profile->base.profiles)) {
112089dbf196SJohn Johansen 			info = "hat not found";
112189dbf196SJohn Johansen 			error = -ENOENT;
112289dbf196SJohn Johansen 			goto fail;
112389dbf196SJohn Johansen 		}
112489dbf196SJohn Johansen 	}
112589dbf196SJohn Johansen 	info = "no hats defined";
112689dbf196SJohn Johansen 	error = -ECHILD;
112789dbf196SJohn Johansen 
112889dbf196SJohn Johansen fail:
112989dbf196SJohn Johansen 	label_for_each_in_ns(it, labels_ns(label), label, profile) {
113089dbf196SJohn Johansen 		/*
113189dbf196SJohn Johansen 		 * no target as it has failed to be found or built
113289dbf196SJohn Johansen 		 *
113389dbf196SJohn Johansen 		 * change_hat uses probing and should not log failures
113489dbf196SJohn Johansen 		 * related to missing hats
113589dbf196SJohn Johansen 		 */
113689dbf196SJohn Johansen 		/* TODO: get rid of GLOBAL_ROOT_UID */
113789dbf196SJohn Johansen 		if (count > 1 || COMPLAIN_MODE(profile)) {
113890c436a6SJohn Johansen 			aa_audit_file(subj_cred, profile, &nullperms,
113990c436a6SJohn Johansen 				      OP_CHANGE_HAT,
114089dbf196SJohn Johansen 				      AA_MAY_CHANGEHAT, name, NULL, NULL,
114189dbf196SJohn Johansen 				      GLOBAL_ROOT_UID, info, error);
114289dbf196SJohn Johansen 		}
114389dbf196SJohn Johansen 	}
114489dbf196SJohn Johansen 	return ERR_PTR(error);
114589dbf196SJohn Johansen 
114689dbf196SJohn Johansen build:
114789dbf196SJohn Johansen 	new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
114890c436a6SJohn Johansen 				   build_change_hat(subj_cred, profile, name,
114990c436a6SJohn Johansen 						    sibling),
115089dbf196SJohn Johansen 				   aa_get_label(&profile->label));
115189dbf196SJohn Johansen 	if (!new) {
115289dbf196SJohn Johansen 		info = "label build failed";
115389dbf196SJohn Johansen 		error = -ENOMEM;
115489dbf196SJohn Johansen 		goto fail;
115589dbf196SJohn Johansen 	} /* else if (IS_ERR) build_change_hat has logged error so return new */
115689dbf196SJohn Johansen 
115789dbf196SJohn Johansen 	return new;
1158898127c3SJohn Johansen }
1159898127c3SJohn Johansen 
1160898127c3SJohn Johansen /**
1161898127c3SJohn Johansen  * aa_change_hat - change hat to/from subprofile
1162898127c3SJohn Johansen  * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0)
1163898127c3SJohn Johansen  * @count: number of hat names in @hats
1164898127c3SJohn Johansen  * @token: magic value to validate the hat change
1165df8073c6SJohn Johansen  * @flags: flags affecting behavior of the change
1166898127c3SJohn Johansen  *
116789dbf196SJohn Johansen  * Returns %0 on success, error otherwise.
116889dbf196SJohn Johansen  *
1169898127c3SJohn Johansen  * Change to the first profile specified in @hats that exists, and store
1170898127c3SJohn Johansen  * the @hat_magic in the current task context.  If the count == 0 and the
1171898127c3SJohn Johansen  * @token matches that stored in the current task context, return to the
1172898127c3SJohn Johansen  * top level profile.
1173898127c3SJohn Johansen  *
117489dbf196SJohn Johansen  * change_hat only applies to profiles in the current ns, and each profile
117589dbf196SJohn Johansen  * in the ns must make the same transition otherwise change_hat will fail.
1176898127c3SJohn Johansen  */
aa_change_hat(const char * hats[],int count,u64 token,int flags)1177df8073c6SJohn Johansen int aa_change_hat(const char *hats[], int count, u64 token, int flags)
1178898127c3SJohn Johansen {
117990c436a6SJohn Johansen 	const struct cred *subj_cred;
11809fcf78ccSJohn Johansen 	struct aa_task_ctx *ctx = task_ctx(current);
118189dbf196SJohn Johansen 	struct aa_label *label, *previous, *new = NULL, *target = NULL;
118289dbf196SJohn Johansen 	struct aa_profile *profile;
11832d679f3cSJohn Johansen 	struct aa_perms perms = {};
118489dbf196SJohn Johansen 	const char *info = NULL;
1185898127c3SJohn Johansen 	int error = 0;
1186898127c3SJohn Johansen 
1187898127c3SJohn Johansen 	/* released below */
118890c436a6SJohn Johansen 	subj_cred = get_current_cred();
118990c436a6SJohn Johansen 	label = aa_get_newest_cred_label(subj_cred);
1190f175221aSJohn Johansen 	previous = aa_get_newest_label(ctx->previous);
1191898127c3SJohn Johansen 
11929fcf78ccSJohn Johansen 	/*
11939fcf78ccSJohn Johansen 	 * Detect no new privs being set, and store the label it
11949fcf78ccSJohn Johansen 	 * occurred under. Ideally this would happen when nnp
11959fcf78ccSJohn Johansen 	 * is set but there isn't a good way to do that yet.
11969fcf78ccSJohn Johansen 	 *
11979fcf78ccSJohn Johansen 	 * Testing for unconfined must be done before the subset test
11989fcf78ccSJohn Johansen 	 */
11999fcf78ccSJohn Johansen 	if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
12009fcf78ccSJohn Johansen 		ctx->nnp = aa_get_label(label);
12019fcf78ccSJohn Johansen 
1202637f688dSJohn Johansen 	if (unconfined(label)) {
120389dbf196SJohn Johansen 		info = "unconfined can not change_hat";
1204898127c3SJohn Johansen 		error = -EPERM;
120589dbf196SJohn Johansen 		goto fail;
1206898127c3SJohn Johansen 	}
1207898127c3SJohn Johansen 
1208898127c3SJohn Johansen 	if (count) {
120990c436a6SJohn Johansen 		new = change_hat(subj_cred, label, hats, count, flags);
121089dbf196SJohn Johansen 		AA_BUG(!new);
121189dbf196SJohn Johansen 		if (IS_ERR(new)) {
121289dbf196SJohn Johansen 			error = PTR_ERR(new);
121389dbf196SJohn Johansen 			new = NULL;
121489dbf196SJohn Johansen 			/* already audited */
1215898127c3SJohn Johansen 			goto out;
1216898127c3SJohn Johansen 		}
1217898127c3SJohn Johansen 
121890c436a6SJohn Johansen 		/* target cred is the same as current except new label */
121990c436a6SJohn Johansen 		error = may_change_ptraced_domain(subj_cred, new, &info);
122089dbf196SJohn Johansen 		if (error)
122189dbf196SJohn Johansen 			goto fail;
1222898127c3SJohn Johansen 
12239fcf78ccSJohn Johansen 		/*
12249fcf78ccSJohn Johansen 		 * no new privs prevents domain transitions that would
12259fcf78ccSJohn Johansen 		 * reduce restrictions.
12269fcf78ccSJohn Johansen 		 */
12279fcf78ccSJohn Johansen 		if (task_no_new_privs(current) && !unconfined(label) &&
12283ed4aaa9SJohn Johansen 		    !aa_label_is_unconfined_subset(new, ctx->nnp)) {
12299fcf78ccSJohn Johansen 			/* not an apparmor denial per se, so don't log it */
12309fcf78ccSJohn Johansen 			AA_DEBUG("no_new_privs - change_hat denied");
12319fcf78ccSJohn Johansen 			error = -EPERM;
12329fcf78ccSJohn Johansen 			goto out;
12339fcf78ccSJohn Johansen 		}
12349fcf78ccSJohn Johansen 
123589dbf196SJohn Johansen 		if (flags & AA_CHANGE_TEST)
123689dbf196SJohn Johansen 			goto out;
1237898127c3SJohn Johansen 
123889dbf196SJohn Johansen 		target = new;
123989dbf196SJohn Johansen 		error = aa_set_current_hat(new, token);
1240898127c3SJohn Johansen 		if (error == -EACCES)
1241898127c3SJohn Johansen 			/* kill task in case of brute force attacks */
124289dbf196SJohn Johansen 			goto kill;
124389dbf196SJohn Johansen 	} else if (previous && !(flags & AA_CHANGE_TEST)) {
12449fcf78ccSJohn Johansen 		/*
12459fcf78ccSJohn Johansen 		 * no new privs prevents domain transitions that would
12469fcf78ccSJohn Johansen 		 * reduce restrictions.
12479fcf78ccSJohn Johansen 		 */
12489fcf78ccSJohn Johansen 		if (task_no_new_privs(current) && !unconfined(label) &&
12493ed4aaa9SJohn Johansen 		    !aa_label_is_unconfined_subset(previous, ctx->nnp)) {
12509fcf78ccSJohn Johansen 			/* not an apparmor denial per se, so don't log it */
12519fcf78ccSJohn Johansen 			AA_DEBUG("no_new_privs - change_hat denied");
12529fcf78ccSJohn Johansen 			error = -EPERM;
12539fcf78ccSJohn Johansen 			goto out;
12549fcf78ccSJohn Johansen 		}
12559fcf78ccSJohn Johansen 
125689dbf196SJohn Johansen 		/* Return to saved label.  Kill task if restore fails
1257898127c3SJohn Johansen 		 * to avoid brute force attacks
1258898127c3SJohn Johansen 		 */
125989dbf196SJohn Johansen 		target = previous;
1260637f688dSJohn Johansen 		error = aa_restore_previous_label(token);
126189dbf196SJohn Johansen 		if (error) {
126289dbf196SJohn Johansen 			if (error == -EACCES)
126389dbf196SJohn Johansen 				goto kill;
126489dbf196SJohn Johansen 			goto fail;
126589dbf196SJohn Johansen 		}
126689dbf196SJohn Johansen 	} /* else ignore @flags && restores when there is no saved profile */
1267898127c3SJohn Johansen 
1268898127c3SJohn Johansen out:
126989dbf196SJohn Johansen 	aa_put_label(new);
127089dbf196SJohn Johansen 	aa_put_label(previous);
1271637f688dSJohn Johansen 	aa_put_label(label);
127290c436a6SJohn Johansen 	put_cred(subj_cred);
1273898127c3SJohn Johansen 
1274898127c3SJohn Johansen 	return error;
127589dbf196SJohn Johansen 
127689dbf196SJohn Johansen kill:
127789dbf196SJohn Johansen 	info = "failed token match";
127889dbf196SJohn Johansen 	perms.kill = AA_MAY_CHANGEHAT;
127989dbf196SJohn Johansen 
128089dbf196SJohn Johansen fail:
128189dbf196SJohn Johansen 	fn_for_each_in_ns(label, profile,
128290c436a6SJohn Johansen 		aa_audit_file(subj_cred, profile, &perms, OP_CHANGE_HAT,
128389dbf196SJohn Johansen 			      AA_MAY_CHANGEHAT, NULL, NULL, target,
128489dbf196SJohn Johansen 			      GLOBAL_ROOT_UID, info, error));
128589dbf196SJohn Johansen 
128689dbf196SJohn Johansen 	goto out;
1287898127c3SJohn Johansen }
1288898127c3SJohn Johansen 
128989dbf196SJohn 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)1290e00b02bbSJohn Johansen static int change_profile_perms_wrapper(const char *op, const char *name,
129190c436a6SJohn Johansen 					const struct cred *subj_cred,
1292e00b02bbSJohn Johansen 					struct aa_profile *profile,
1293e00b02bbSJohn Johansen 					struct aa_label *target, bool stack,
1294e00b02bbSJohn Johansen 					u32 request, struct aa_perms *perms)
1295e00b02bbSJohn Johansen {
12961ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
12971ad22fccSJohn Johansen 						    typeof(*rules), list);
1298e00b02bbSJohn Johansen 	const char *info = NULL;
1299e00b02bbSJohn Johansen 	int error = 0;
1300e00b02bbSJohn Johansen 
1301e00b02bbSJohn Johansen 	if (!error)
1302e00b02bbSJohn Johansen 		error = change_profile_perms(profile, target, stack, request,
130398b824ffSJohn Johansen 					     rules->file->start[AA_CLASS_FILE],
130453bdc46fSJohn Johansen 					     perms);
1305e00b02bbSJohn Johansen 	if (error)
130690c436a6SJohn Johansen 		error = aa_audit_file(subj_cred, profile, perms, op, request,
130790c436a6SJohn Johansen 				      name,
1308e00b02bbSJohn Johansen 				      NULL, target, GLOBAL_ROOT_UID, info,
1309e00b02bbSJohn Johansen 				      error);
1310e00b02bbSJohn Johansen 
1311e00b02bbSJohn Johansen 	return error;
1312e00b02bbSJohn Johansen }
131389dbf196SJohn Johansen 
1314*3c49ce0eSJohn Johansen static const char *stack_msg = "change_profile unprivileged unconfined converted to stacking";
13152d9da9b1SJohn Johansen 
1316898127c3SJohn Johansen /**
1317898127c3SJohn Johansen  * aa_change_profile - perform a one-way profile transition
1318aa9a39adSJohn Johansen  * @fqname: name of profile may include namespace (NOT NULL)
1319df8073c6SJohn Johansen  * @flags: flags affecting change behavior
1320898127c3SJohn Johansen  *
1321898127c3SJohn Johansen  * Change to new profile @name.  Unlike with hats, there is no way
1322898127c3SJohn Johansen  * to change back.  If @name isn't specified the current profile name is
1323898127c3SJohn Johansen  * used.
1324898127c3SJohn Johansen  * If @onexec then the transition is delayed until
1325898127c3SJohn Johansen  * the next exec.
1326898127c3SJohn Johansen  *
1327898127c3SJohn Johansen  * Returns %0 on success, error otherwise.
1328898127c3SJohn Johansen  */
aa_change_profile(const char * fqname,int flags)1329df8073c6SJohn Johansen int aa_change_profile(const char *fqname, int flags)
1330898127c3SJohn Johansen {
1331e00b02bbSJohn Johansen 	struct aa_label *label, *new = NULL, *target = NULL;
1332e00b02bbSJohn Johansen 	struct aa_profile *profile;
13332d679f3cSJohn Johansen 	struct aa_perms perms = {};
1334e00b02bbSJohn Johansen 	const char *info = NULL;
1335e00b02bbSJohn Johansen 	const char *auditname = fqname;		/* retain leading & if stack */
1336e00b02bbSJohn Johansen 	bool stack = flags & AA_CHANGE_STACK;
13379fcf78ccSJohn Johansen 	struct aa_task_ctx *ctx = task_ctx(current);
133890c436a6SJohn Johansen 	const struct cred *subj_cred = get_current_cred();
133947f6e5ccSJohn Johansen 	int error = 0;
1340e00b02bbSJohn Johansen 	char *op;
1341898127c3SJohn Johansen 	u32 request;
1342898127c3SJohn Johansen 
13439fcf78ccSJohn Johansen 	label = aa_get_current_label();
13449fcf78ccSJohn Johansen 
13459fcf78ccSJohn Johansen 	/*
13469fcf78ccSJohn Johansen 	 * Detect no new privs being set, and store the label it
13479fcf78ccSJohn Johansen 	 * occurred under. Ideally this would happen when nnp
13489fcf78ccSJohn Johansen 	 * is set but there isn't a good way to do that yet.
13499fcf78ccSJohn Johansen 	 *
13509fcf78ccSJohn Johansen 	 * Testing for unconfined must be done before the subset test
13519fcf78ccSJohn Johansen 	 */
13529fcf78ccSJohn Johansen 	if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
13539fcf78ccSJohn Johansen 		ctx->nnp = aa_get_label(label);
13549fcf78ccSJohn Johansen 
1355aa9a39adSJohn Johansen 	if (!fqname || !*fqname) {
1356a0b845ffSXiyu Yang 		aa_put_label(label);
1357aa9a39adSJohn Johansen 		AA_DEBUG("no profile name");
1358898127c3SJohn Johansen 		return -EINVAL;
1359aa9a39adSJohn Johansen 	}
1360898127c3SJohn Johansen 
1361df8073c6SJohn Johansen 	if (flags & AA_CHANGE_ONEXEC) {
1362898127c3SJohn Johansen 		request = AA_MAY_ONEXEC;
1363e00b02bbSJohn Johansen 		if (stack)
1364e00b02bbSJohn Johansen 			op = OP_STACK_ONEXEC;
1365e00b02bbSJohn Johansen 		else
1366898127c3SJohn Johansen 			op = OP_CHANGE_ONEXEC;
1367898127c3SJohn Johansen 	} else {
1368898127c3SJohn Johansen 		request = AA_MAY_CHANGE_PROFILE;
1369e00b02bbSJohn Johansen 		if (stack)
1370e00b02bbSJohn Johansen 			op = OP_STACK;
1371e00b02bbSJohn Johansen 		else
1372898127c3SJohn Johansen 			op = OP_CHANGE_PROFILE;
1373898127c3SJohn Johansen 	}
1374898127c3SJohn Johansen 
13752d9da9b1SJohn Johansen 	/* This should move to a per profile test. Requires pushing build
13762d9da9b1SJohn Johansen 	 * into callback
13772d9da9b1SJohn Johansen 	 */
13782d9da9b1SJohn Johansen 	if (!stack && unconfined(label) &&
13792d9da9b1SJohn Johansen 	    label == &labels_ns(label)->unconfined->label &&
13802d9da9b1SJohn Johansen 	    aa_unprivileged_unconfined_restricted &&
13812d9da9b1SJohn Johansen 	    /* TODO: refactor so this check is a fn */
13822d9da9b1SJohn Johansen 	    cap_capable(current_cred(), &init_user_ns, CAP_MAC_OVERRIDE,
13832d9da9b1SJohn Johansen 			CAP_OPT_NOAUDIT)) {
13842d9da9b1SJohn Johansen 		/* regardless of the request in this case apparmor
13852d9da9b1SJohn Johansen 		 * stacks against unconfined so admin set policy can't be
13862d9da9b1SJohn Johansen 		 * by-passed
13872d9da9b1SJohn Johansen 		 */
13882d9da9b1SJohn Johansen 		stack = true;
13892d9da9b1SJohn Johansen 		perms.audit = request;
13902d9da9b1SJohn Johansen 		(void) fn_for_each_in_ns(label, profile,
13912d9da9b1SJohn Johansen 				aa_audit_file(subj_cred, profile, &perms, op,
13922d9da9b1SJohn Johansen 					      request, auditname, NULL, target,
13932d9da9b1SJohn Johansen 					      GLOBAL_ROOT_UID, stack_msg, 0));
13942d9da9b1SJohn Johansen 		perms.audit = 0;
13952d9da9b1SJohn Johansen 	}
13962d9da9b1SJohn Johansen 
1397e00b02bbSJohn Johansen 	if (*fqname == '&') {
1398e00b02bbSJohn Johansen 		stack = true;
1399e00b02bbSJohn Johansen 		/* don't have label_parse() do stacking */
1400e00b02bbSJohn Johansen 		fqname++;
1401c29bceb3SJohn Johansen 	}
1402e00b02bbSJohn Johansen 	target = aa_label_parse(label, fqname, GFP_KERNEL, true, false);
1403e00b02bbSJohn Johansen 	if (IS_ERR(target)) {
1404e00b02bbSJohn Johansen 		struct aa_profile *tprofile;
1405c29bceb3SJohn Johansen 
1406e00b02bbSJohn Johansen 		info = "label not found";
1407e00b02bbSJohn Johansen 		error = PTR_ERR(target);
1408e00b02bbSJohn Johansen 		target = NULL;
1409e00b02bbSJohn Johansen 		/*
1410e00b02bbSJohn Johansen 		 * TODO: fixme using labels_profile is not right - do profile
1411e00b02bbSJohn Johansen 		 * per complain profile
1412e00b02bbSJohn Johansen 		 */
1413df8073c6SJohn Johansen 		if ((flags & AA_CHANGE_TEST) ||
1414e00b02bbSJohn Johansen 		    !COMPLAIN_MODE(labels_profile(label)))
1415898127c3SJohn Johansen 			goto audit;
1416898127c3SJohn Johansen 		/* released below */
141758f89ce5SJohn Johansen 		tprofile = aa_new_learning_profile(labels_profile(label), false,
1418e00b02bbSJohn Johansen 						   fqname, GFP_KERNEL);
1419e00b02bbSJohn Johansen 		if (!tprofile) {
1420898127c3SJohn Johansen 			info = "failed null profile create";
1421898127c3SJohn Johansen 			error = -ENOMEM;
1422898127c3SJohn Johansen 			goto audit;
1423898127c3SJohn Johansen 		}
1424e00b02bbSJohn Johansen 		target = &tprofile->label;
1425e00b02bbSJohn Johansen 		goto check;
1426898127c3SJohn Johansen 	}
1427898127c3SJohn Johansen 
1428e00b02bbSJohn Johansen 	/*
1429e00b02bbSJohn Johansen 	 * self directed transitions only apply to current policy ns
1430e00b02bbSJohn Johansen 	 * TODO: currently requiring perms for stacking and straight change
1431e00b02bbSJohn Johansen 	 *       stacking doesn't strictly need this. Determine how much
1432e00b02bbSJohn Johansen 	 *       we want to loosen this restriction for stacking
1433e00b02bbSJohn Johansen 	 *
1434e00b02bbSJohn Johansen 	 * if (!stack) {
1435e00b02bbSJohn Johansen 	 */
1436e00b02bbSJohn Johansen 	error = fn_for_each_in_ns(label, profile,
1437e00b02bbSJohn Johansen 			change_profile_perms_wrapper(op, auditname,
143890c436a6SJohn Johansen 						     subj_cred,
1439e00b02bbSJohn Johansen 						     profile, target, stack,
1440e00b02bbSJohn Johansen 						     request, &perms));
1441e00b02bbSJohn Johansen 	if (error)
1442e00b02bbSJohn Johansen 		/* auditing done in change_profile_perms_wrapper */
1443e00b02bbSJohn Johansen 		goto out;
1444aa9a39adSJohn Johansen 
1445e00b02bbSJohn Johansen 	/* } */
1446e00b02bbSJohn Johansen 
1447e00b02bbSJohn Johansen check:
1448898127c3SJohn Johansen 	/* check if tracing task is allowed to trace target domain */
144990c436a6SJohn Johansen 	error = may_change_ptraced_domain(subj_cred, target, &info);
1450e00b02bbSJohn Johansen 	if (error && !fn_for_each_in_ns(label, profile,
1451e00b02bbSJohn Johansen 					COMPLAIN_MODE(profile)))
1452e00b02bbSJohn Johansen 		goto audit;
1453e00b02bbSJohn Johansen 
1454e00b02bbSJohn Johansen 	/* TODO: add permission check to allow this
1455e00b02bbSJohn Johansen 	 * if ((flags & AA_CHANGE_ONEXEC) && !current_is_single_threaded()) {
1456e00b02bbSJohn Johansen 	 *      info = "not a single threaded task";
1457e00b02bbSJohn Johansen 	 *      error = -EACCES;
1458e00b02bbSJohn Johansen 	 *      goto audit;
1459e00b02bbSJohn Johansen 	 * }
1460e00b02bbSJohn Johansen 	 */
1461e00b02bbSJohn Johansen 	if (flags & AA_CHANGE_TEST)
1462e00b02bbSJohn Johansen 		goto out;
1463e00b02bbSJohn Johansen 
14649fcf78ccSJohn Johansen 	/* stacking is always a subset, so only check the nonstack case */
14659fcf78ccSJohn Johansen 	if (!stack) {
14669fcf78ccSJohn Johansen 		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
14679fcf78ccSJohn Johansen 					   aa_get_label(target),
14689fcf78ccSJohn Johansen 					   aa_get_label(&profile->label));
14699fcf78ccSJohn Johansen 		/*
14709fcf78ccSJohn Johansen 		 * no new privs prevents domain transitions that would
14719fcf78ccSJohn Johansen 		 * reduce restrictions.
14729fcf78ccSJohn Johansen 		 */
14739fcf78ccSJohn Johansen 		if (task_no_new_privs(current) && !unconfined(label) &&
14743ed4aaa9SJohn Johansen 		    !aa_label_is_unconfined_subset(new, ctx->nnp)) {
14759fcf78ccSJohn Johansen 			/* not an apparmor denial per se, so don't log it */
14769fcf78ccSJohn Johansen 			AA_DEBUG("no_new_privs - change_hat denied");
14779fcf78ccSJohn Johansen 			error = -EPERM;
14789fcf78ccSJohn Johansen 			goto out;
14799fcf78ccSJohn Johansen 		}
14809fcf78ccSJohn Johansen 	}
14819fcf78ccSJohn Johansen 
1482e00b02bbSJohn Johansen 	if (!(flags & AA_CHANGE_ONEXEC)) {
1483e00b02bbSJohn Johansen 		/* only transition profiles in the current ns */
1484e00b02bbSJohn Johansen 		if (stack)
1485e00b02bbSJohn Johansen 			new = aa_label_merge(label, target, GFP_KERNEL);
1486e00b02bbSJohn Johansen 		if (IS_ERR_OR_NULL(new)) {
1487e00b02bbSJohn Johansen 			info = "failed to build target label";
1488d6d478aeSJohn Johansen 			if (!new)
1489d6d478aeSJohn Johansen 				error = -ENOMEM;
1490d6d478aeSJohn Johansen 			else
1491e00b02bbSJohn Johansen 				error = PTR_ERR(new);
1492e00b02bbSJohn Johansen 			new = NULL;
1493e00b02bbSJohn Johansen 			perms.allow = 0;
1494898127c3SJohn Johansen 			goto audit;
1495898127c3SJohn Johansen 		}
1496e00b02bbSJohn Johansen 		error = aa_replace_current_label(new);
14979fcf78ccSJohn Johansen 	} else {
14989fcf78ccSJohn Johansen 		if (new) {
14999fcf78ccSJohn Johansen 			aa_put_label(new);
15009fcf78ccSJohn Johansen 			new = NULL;
15019fcf78ccSJohn Johansen 		}
15029fcf78ccSJohn Johansen 
1503e00b02bbSJohn Johansen 		/* full transition will be built in exec path */
15040897fcb1SQuanfa Fu 		aa_set_current_onexec(target, stack);
15059fcf78ccSJohn Johansen 	}
1506898127c3SJohn Johansen 
1507898127c3SJohn Johansen audit:
1508e00b02bbSJohn Johansen 	error = fn_for_each_in_ns(label, profile,
150990c436a6SJohn Johansen 			aa_audit_file(subj_cred,
151090c436a6SJohn Johansen 				      profile, &perms, op, request, auditname,
1511e00b02bbSJohn Johansen 				      NULL, new ? new : target,
1512e00b02bbSJohn Johansen 				      GLOBAL_ROOT_UID, info, error));
1513898127c3SJohn Johansen 
1514e00b02bbSJohn Johansen out:
1515e00b02bbSJohn Johansen 	aa_put_label(new);
1516e00b02bbSJohn Johansen 	aa_put_label(target);
1517637f688dSJohn Johansen 	aa_put_label(label);
151890c436a6SJohn Johansen 	put_cred(subj_cred);
1519898127c3SJohn Johansen 
1520898127c3SJohn Johansen 	return error;
1521898127c3SJohn Johansen }
1522