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