apparmor: split xxx_in_ns into its two separate semantic use cases

This patch doesn't change current functionality, it switches the two
uses of the in_ns fns and macros into the two semantically different
cases they are used for.

xxx_in_scope for checking mediation interaction between profiles
xxx_in_view to determine which profiles are visible.The scope will
always be a subset of the view as profiles that can not see each
other can not interact.

The split can not be completely done for label_match because it has to
distinct uses matching permission against label in scope, and checking
if a transition to a profile is allowed. The transition to a profile
can include profiles that are in view but not in scope, so retain this
distinction as a parameter.

While at the moment the two uses are very similar, in the future there
will be additional differences. So make sure the semantics differences
are present in the code.

Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
John Johansen
2025-12-25 01:21:23 -08:00
parent a4c9efa4db
commit 796c146fa6
5 changed files with 61 additions and 46 deletions

View File

@@ -416,7 +416,7 @@ static int profile_peer_perm(struct aa_profile *profile, u32 request,
unix_sk(sk),
peer_addr, peer_addrlen, &p, &ad->info);
return fn_for_each_in_ns(peer_label, peerp,
return fn_for_each_in_scope(peer_label, peerp,
match_label(profile, rules, state, request,
peerp, p, ad));
}

View File

@@ -801,7 +801,7 @@ static ssize_t query_label(char *buf, size_t buf_len,
perms = allperms;
if (view_only) {
label_for_each_in_ns(i, labels_ns(label), label, profile) {
label_for_each_in_scope(i, labels_ns(label), label, profile) {
profile_query_cb(profile, &perms, match_str, match_len);
}
} else {

View File

@@ -115,7 +115,7 @@ static inline aa_state_t match_component(struct aa_profile *profile,
* @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
* @inview: whether to match labels in view or only in scope
* @request: permissions to request
* @perms: perms struct to set
*
@@ -127,7 +127,7 @@ static inline aa_state_t match_component(struct aa_profile *profile,
*/
static int label_compound_match(struct aa_profile *profile,
struct aa_label *label, bool stack,
aa_state_t state, bool subns, u32 request,
aa_state_t state, bool inview, u32 request,
struct aa_perms *perms)
{
struct aa_ruleset *rules = profile->label.rules[0];
@@ -135,9 +135,9 @@ static int label_compound_match(struct aa_profile *profile,
struct label_it i;
struct path_cond cond = { };
/* find first subcomponent that is visible */
/* find first subcomponent that is in view and going to be interated with */
label_for_each(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
state = match_component(profile, tp, stack, state);
if (!state)
@@ -151,7 +151,7 @@ static int label_compound_match(struct aa_profile *profile,
next:
label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
state = aa_dfa_match(rules->file->dfa, state, "//&");
state = match_component(profile, tp, false, state);
@@ -177,7 +177,7 @@ fail:
* @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
* @inview: whether to match labels in view or only in scope
* @request: permissions to request
* @perms: an initialized perms struct to add accumulation to
*
@@ -189,7 +189,7 @@ fail:
*/
static int label_components_match(struct aa_profile *profile,
struct aa_label *label, bool stack,
aa_state_t start, bool subns, u32 request,
aa_state_t start, bool inview, u32 request,
struct aa_perms *perms)
{
struct aa_ruleset *rules = profile->label.rules[0];
@@ -201,7 +201,7 @@ static int label_components_match(struct aa_profile *profile,
/* find first subcomponent to test */
label_for_each(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
state = match_component(profile, tp, stack, start);
if (!state)
@@ -218,7 +218,7 @@ next:
aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp);
label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
state = match_component(profile, tp, stack, start);
if (!state)
@@ -245,26 +245,26 @@ fail:
* @label: label to match (NOT NULL)
* @stack: whether this is a stacking request
* @state: state to start in
* @subns: whether to match subns components
* @inview: whether to match labels in view or only in scope
* @request: permission request
* @perms: Returns computed perms (NOT NULL)
*
* Returns: the state the match finished in, may be the none matching state
*/
static int label_match(struct aa_profile *profile, struct aa_label *label,
bool stack, aa_state_t state, bool subns, u32 request,
bool stack, aa_state_t state, bool inview, u32 request,
struct aa_perms *perms)
{
int error;
*perms = nullperms;
error = label_compound_match(profile, label, stack, state, subns,
error = label_compound_match(profile, label, stack, state, inview,
request, perms);
if (!error)
return error;
*perms = allperms;
return label_components_match(profile, label, stack, state, subns,
return label_components_match(profile, label, stack, state, inview,
request, perms);
}
@@ -880,14 +880,16 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
AA_BUG(!bprm);
AA_BUG(!buffer);
/* TODO: determine how much we want to loosen this */
error = fn_for_each_in_ns(label, profile,
/* TODO: determine how much we want to loosen this
* only check profiles in scope for permission to change at exec
*/
error = fn_for_each_in_scope(label, profile,
profile_onexec(subj_cred, profile, onexec, stack,
bprm, buffer, cond, unsafe));
if (error)
return ERR_PTR(error);
new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
stack ? aa_label_merge(&profile->label, onexec,
GFP_KERNEL)
: aa_get_newest_label(onexec),
@@ -897,7 +899,7 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
return new;
/* TODO: get rid of GLOBAL_ROOT_UID */
error = fn_for_each_in_ns(label, profile,
error = fn_for_each_in_scope(label, profile,
aa_audit_file(subj_cred, profile, &nullperms,
OP_CHANGE_ONEXEC,
AA_MAY_ONEXEC, bprm->filename, NULL,
@@ -1123,7 +1125,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
/*find first matching hat */
for (i = 0; i < count && !hat; i++) {
name = hats[i];
label_for_each_in_ns(it, labels_ns(label), label, profile) {
label_for_each_in_scope(it, labels_ns(label), label, profile) {
if (sibling && PROFILE_IS_HAT(profile)) {
root = aa_get_profile_rcu(&profile->parent);
} else if (!sibling && !PROFILE_IS_HAT(profile)) {
@@ -1159,7 +1161,7 @@ outer_continue:
* change_hat.
*/
name = NULL;
label_for_each_in_ns(it, labels_ns(label), label, profile) {
label_for_each_in_scope(it, labels_ns(label), label, profile) {
if (!list_empty(&profile->base.profiles)) {
info = "hat not found";
error = -ENOENT;
@@ -1170,7 +1172,7 @@ outer_continue:
error = -ECHILD;
fail:
label_for_each_in_ns(it, labels_ns(label), label, profile) {
label_for_each_in_scope(it, labels_ns(label), label, profile) {
/*
* no target as it has failed to be found or built
*
@@ -1188,7 +1190,7 @@ fail:
return ERR_PTR(error);
build:
new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
build_change_hat(subj_cred, profile, name,
sibling),
aa_get_label(&profile->label));
@@ -1251,7 +1253,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
bool empty = true;
rcu_read_lock();
label_for_each_in_ns(i, labels_ns(label), label, profile) {
label_for_each_in_scope(i, labels_ns(label), label, profile) {
empty &= list_empty(&profile->base.profiles);
}
rcu_read_unlock();
@@ -1338,7 +1340,7 @@ kill:
perms.kill = AA_MAY_CHANGEHAT;
fail:
fn_for_each_in_ns(label, profile,
fn_for_each_in_scope(label, profile,
aa_audit_file(subj_cred, profile, &perms, OP_CHANGE_HAT,
AA_MAY_CHANGEHAT, NULL, NULL, target,
GLOBAL_ROOT_UID, info, error));
@@ -1446,7 +1448,7 @@ int aa_change_profile(const char *fqname, int flags)
*/
stack = true;
perms.audit = request;
(void) fn_for_each_in_ns(label, profile,
(void) fn_for_each_in_scope(label, profile,
aa_audit_file(subj_cred, profile, &perms, op,
request, auditname, NULL, target,
GLOBAL_ROOT_UID, stack_msg, 0));
@@ -1492,7 +1494,7 @@ int aa_change_profile(const char *fqname, int flags)
*
* if (!stack) {
*/
error = fn_for_each_in_ns(label, profile,
error = fn_for_each_in_scope(label, profile,
change_profile_perms_wrapper(op, auditname,
subj_cred,
profile, target, stack,
@@ -1506,7 +1508,7 @@ int aa_change_profile(const char *fqname, int flags)
check:
/* check if tracing task is allowed to trace target domain */
error = may_change_ptraced_domain(subj_cred, target, &info);
if (error && !fn_for_each_in_ns(label, profile,
if (error && !fn_for_each_in_scope(label, profile,
COMPLAIN_MODE(profile)))
goto audit;
@@ -1522,7 +1524,7 @@ check:
/* stacking is always a subset, so only check the nonstack case */
if (!stack) {
new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
aa_get_label(target),
aa_get_label(&profile->label));
/*
@@ -1565,7 +1567,7 @@ check:
}
audit:
error = fn_for_each_in_ns(label, profile,
error = fn_for_each_in_scope(label, profile,
aa_audit_file(subj_cred,
profile, &perms, op, request, auditname,
NULL, new ? new : target,

View File

@@ -80,6 +80,19 @@ int aa_print_debug_params(char *buffer);
/* Flag indicating whether initialization completed */
extern int apparmor_initialized;
/* semantic split of scope and view */
#define aa_in_scope(SUBJ, OBJ) \
aa_ns_visible(SUBJ, OBJ, false)
#define aa_in_view(SUBJ, OBJ) \
aa_ns_visible(SUBJ, OBJ, true)
#define label_for_each_in_scope(I, NS, L, P) \
label_for_each_in_ns(I, NS, L, P)
#define fn_for_each_in_scope(L, P, FN) \
fn_for_each_in_ns(L, P, FN)
/* fn's in lib */
const char *skipn_spaces(const char *str, size_t n);
const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
@@ -316,7 +329,7 @@ __done: \
})
#define __fn_build_in_ns(NS, P, NS_FN, OTHER_FN) \
#define __fn_build_in_scope(NS, P, NS_FN, OTHER_FN) \
({ \
struct aa_label *__new; \
if ((P)->ns != (NS)) \
@@ -326,10 +339,10 @@ __done: \
(__new); \
})
#define fn_label_build_in_ns(L, P, GFP, NS_FN, OTHER_FN) \
#define fn_label_build_in_scope(L, P, GFP, NS_FN, OTHER_FN) \
({ \
fn_label_build((L), (P), (GFP), \
__fn_build_in_ns(labels_ns(L), (P), (NS_FN), (OTHER_FN))); \
__fn_build_in_scope(labels_ns(L), (P), (NS_FN), (OTHER_FN))); \
})
#endif /* __AA_LIB_H */

View File

@@ -1274,7 +1274,7 @@ static inline aa_state_t match_component(struct aa_profile *profile,
* @rules: ruleset to search
* @label: label to check access permissions for
* @state: state to start match in
* @subns: whether to do permission checks on components in a subns
* @inview: whether to match labels in view or only in scope
* @request: permissions to request
* @perms: perms struct to set
*
@@ -1287,7 +1287,7 @@ static inline aa_state_t match_component(struct aa_profile *profile,
static int label_compound_match(struct aa_profile *profile,
struct aa_ruleset *rules,
struct aa_label *label,
aa_state_t state, bool subns, u32 request,
aa_state_t state, bool inview, u32 request,
struct aa_perms *perms)
{
struct aa_profile *tp;
@@ -1295,7 +1295,7 @@ static int label_compound_match(struct aa_profile *profile,
/* find first subcomponent that is visible */
label_for_each(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
state = match_component(profile, rules, tp, state);
if (!state)
@@ -1309,7 +1309,7 @@ static int label_compound_match(struct aa_profile *profile,
next:
label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
state = aa_dfa_match(rules->policy->dfa, state, "//&");
state = match_component(profile, rules, tp, state);
@@ -1330,7 +1330,7 @@ fail:
* @rules: ruleset to search
* @label: label to check access permissions for
* @start: state to start match in
* @subns: whether to do permission checks on components in a subns
* @subns: whether to match labels in view or only in scope
* @request: permissions to request
* @perms: an initialized perms struct to add accumulation to
*
@@ -1343,7 +1343,7 @@ fail:
static int label_components_match(struct aa_profile *profile,
struct aa_ruleset *rules,
struct aa_label *label, aa_state_t start,
bool subns, u32 request,
bool inview, u32 request,
struct aa_perms *perms)
{
struct aa_profile *tp;
@@ -1353,7 +1353,7 @@ static int label_components_match(struct aa_profile *profile,
/* find first subcomponent to test */
label_for_each(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
state = match_component(profile, rules, tp, start);
if (!state)
@@ -1368,7 +1368,7 @@ next:
tmp = *aa_lookup_perms(rules->policy, state);
aa_perms_accum(perms, &tmp);
label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
state = match_component(profile, rules, tp, start);
if (!state)
@@ -1393,24 +1393,24 @@ fail:
* @rules: ruleset to search
* @label: label to match (NOT NULL)
* @state: state to start in
* @subns: whether to match subns components
* @subns: whether to match labels in view or only in scope
* @request: permission request
* @perms: Returns computed perms (NOT NULL)
*
* Returns: the state the match finished in, may be the none matching state
*/
int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
struct aa_label *label, aa_state_t state, bool subns,
struct aa_label *label, aa_state_t state, bool inview,
u32 request, struct aa_perms *perms)
{
aa_state_t tmp = label_compound_match(profile, rules, label, state, subns,
request, perms);
aa_state_t tmp = label_compound_match(profile, rules, label, state,
inview, request, perms);
if ((perms->allow & request) == request)
return 0;
/* failed compound_match try component matches */
*perms = allperms;
return label_components_match(profile, rules, label, state, subns,
return label_components_match(profile, rules, label, state, inview,
request, perms);
}