// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
* This file contains AppArmor policy attachment and domain transitions
*
* Copyright (C) 2002-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
*/
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/syscalls.h>
#include <linux/personality.h>
#include <linux/xattr.h>
#include <linux/user_namespace.h>
#include "include/audit.h"
#include "include/apparmorfs.h"
#include "include/cred.h"
#include "include/domain.h"
#include "include/file.h"
#include "include/ipc.h"
#include "include/match.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/policy_ns.h"
static const char * const CONFLICTING_ATTACH_STR = "conflicting profile attachments";
static const char * const CONFLICTING_ATTACH_STR_IX =
"conflicting profile attachments - ix fallback";
static const char * const CONFLICTING_ATTACH_STR_UX =
"conflicting profile attachments - ux fallback";
/**
* may_change_ptraced_domain - check if can change profile on ptraced task
* @to_cred: cred of task changing domain
* @to_label: profile to change to (NOT NULL)
* @info: message if there is an error
*
* Check if current is ptraced and if so if the tracing task is allowed
* to trace the new domain
*
* Returns: %0 or error if change not allowed
*/
static int may_change_ptraced_domain(const struct cred *to_cred,
struct aa_label *to_label,
const char **info)
{
struct task_struct *tracer;
struct aa_label *tracerl = NULL;
const struct cred *tracer_cred = NULL;
int error = 0;
rcu_read_lock();
tracer = ptrace_parent(current);
if (tracer) {
/* released below */
tracerl = aa_get_task_label(tracer);
tracer_cred = get_task_cred(tracer);
}
/* not ptraced */
if (!tracer || unconfined(tracerl))
goto out;
error = aa_may_ptrace(tracer_cred, tracerl, to_cred, to_label,
PTRACE_MODE_ATTACH);
out:
rcu_read_unlock();
aa_put_label(tracerl);
put_cred(tracer_cred);
if (error)
*info = "ptrace prevents transition";
return error;
}
/**** TODO: dedup to aa_label_match - needs perm and dfa, merging
* specifically this is an exact copy of aa_label_match except
* aa_compute_perms is replaced with aa_compute_fperms
* and policy->dfa with file->dfa
****/
/* match a profile and its associated ns component if needed
* Assumes visibility test has already been done.
* If a subns profile is not to be matched should be prescreened with
* visibility test.
*/
static inline aa_state_t match_component(struct aa_profile *profile,
struct aa_profile *tp,
bool stack, aa_state_t state)
{
struct aa_ruleset *rules = profile->label.rules[0];
const char *ns_name;
if (stack)
state = aa_dfa_match(rules->file->dfa, state, "&");
if (profile->ns == tp->ns)
return aa_dfa_match(rules->file->dfa, state, tp->base.hname);
/* try matching with namespace name and then profile */
ns_name = aa_ns_name(profile->ns, tp->ns, true);
state = aa_dfa_match_len(rules->file->dfa, state, ":", 1);
state = aa_dfa_match(rules->file->dfa, state, ns_name);
state = aa_dfa_match_len(rules->file->dfa, state, ":", 1);
return aa_dfa_match(rules->file->dfa, state, tp->base.hname);
}
/**
* label_compound_match - find perms for full compound label
* @profile: profile to find perms for
* @label: label to check access permissions for
* @stack: whether this is a stacking request
* @state: state to start match in
* @subns: whether to do permission checks on components in a subns
* @request: permissions to request
* @perms: perms struct to set
*
* Returns: 0 on success else ERROR
*
* For the label A//&B//&C this does the perm match for A//&B//&C
* @perms should be preinitialized with allperms OR a previous permission
* check to be stacked.
*/
static int 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)
{
struct aa_ruleset *rules = profile->label.rules[0];
struct aa_profile *tp;
struct label_it i;
struct path_cond cond = { };
/* find first subcomponent that is visible */
label_for_each(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
continue;
state = match_component(profile, tp, stack, state);
if (!state)
goto fail;
goto next;
}
/* no component visible */
*perms = allperms;
return 0;
next:
label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
continue;
state = aa_dfa_match(rules->file->dfa, state, "//&");
state = match_component(profile, tp, false, state);
if (!state)
goto fail;
}
*perms = *(aa_lookup_condperms(current_fsuid(), rules->file, state,
&cond));
aa_apply_modes_to_perms(profile, perms);
if ((perms->allow & request) != request)
return -EACCES;
return 0;
fail:
*perms = nullperms;
return -EACCES;
}
/**
* label_components_match - find perms for all subcomponents of a label
* @profile: profile to find perms for
* @label: label to check access permissions for
* @stack: whether this is a stacking request
* @start: state to start match in
* @subns: whether to do permission checks on components in a subns
* @request: permissions to request
* @perms: an initialized perms struct to add accumulation to
*
* Returns: 0 on success else ERROR
*
* For the label A//&B//&C this does the perm match for each of A and B and C
* @perms should be preinitialized with allperms OR a previous permission
* check to be stacked.
*/
static int 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)
{
struct aa_ruleset *rules = profile->label.rules[0];
struct aa_profile *tp;
struct label_it i;
struct aa_perms tmp;
struct path_cond cond = { };
aa_state_t state = 0;
/* find first subcomponent to test */
label_for_e
|