// 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/mutex.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/lib.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(const struct aa_profile *profile, const 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 * @inview: whether to match labels in view or only in scope * @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(const struct aa_profile *profile, struct aa_label *label, bool stack, aa_state_t state, bool inview, 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 in view and going to be interacted with */ label_for_each(i, label, tp) { if (!aa_ns_visible(profile->ns, tp->ns, inview)) 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, inview)) 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 * @inview: whether to match labels in view or only in scope * @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(const struct aa_profile *profile, struct aa_label *label, bool stack, aa_state_t start, bool inview, 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_each(i, label, tp) { if (!aa_ns_visible(profile->ns, tp->ns, inview)) continue; state = match_component(profile, tp, stack, start); if (!state) goto fail; goto next; } /* no subcomponents visible - no change in perms */ return 0; next: tmp = *(aa_lookup_condperms(current_fsuid(), rules->file, state, &cond)); 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, inview)) continue; state = match_component(profile, tp, stack, start); if (!state) goto fail; tmp = *(aa_lookup_condperms(current_fsuid(), rules->file, state, &cond)); aa_apply_modes_to_perms(profile, &tmp); aa_perms_accum(perms, &tmp); } if ((perms->allow & request) != request) return -EACCES; return 0; fail: *perms = nullperms; return -EACCES;} /** * label_match - do a multi-component label match * @profile: profile to match against (NOT NULL) * @label: label to match (NOT NULL) * @stack: whether this is a stacking request * @state: state to start in * @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(const struct aa_profile *profile, struct aa_label *label, 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, inview, request, perms); if (!error) return error; *perms = allperms; return label_components_match(profile, label, stack, state, inview, request, perms);} /******* end TODO: dedup *****/ /** * change_profile_perms - find permissions for change_profile * @profile: the current profile (NOT NULL) * @target: label to transition to (NOT NULL) * @stack: whether this is a stacking request * @request: requested perms * @start: state to start matching in * @perms: Returns computed perms (NOT NULL) * * * Returns: permission set * * currently only matches full label A//&B//&C or individual components A, B, C * not arbitrary combinations. Eg. A//&B, C */static int change_profile_perms(const struct aa_profile *profile, struct aa_label *target, bool stack, u32 request, aa_state_t start, struct aa_perms *perms){ if (profile_unconfined(profile)) { perms->allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC; perms->audit = perms->quiet = perms->kill = 0; return 0; } /* TODO: add profile in ns screening */ return label_match(profile, target, stack, start, true, request, perms);} /** * aa_xattrs_match - check whether a file matches the xattrs defined in profile * @path: path for file being matched (NOT NULL) * @profile: profile to match against (NOT NULL) * @state: state to start match in * * Returns: number of extended attributes that matched, or < 0 on error */static int aa_xattrs_match(const struct path *path, const struct aa_profile *profile, aa_state_t state){ AA_BUG(!path); AA_BUG(!profile); int i; struct dentry *d; char *value = NULL; const struct aa_attachment *attach = &profile->attach; int size, value_size = 0, ret = attach->xattr_count; if (!attach->xattr_count) return 0; might_sleep(); /* transition from exec match to xattr set */ state = aa_dfa_outofband_transition(attach->xmatch->dfa, state); d = path->dentry; for (i = 0; i < attach->xattr_count; i++) { size = vfs_getxattr_alloc(&nop_mnt_idmap, d, attach->xattrs[i], &value, value_size, GFP_KERNEL); if (size >= 0) { struct aa_perms *perms; /* * Check the xattr presence before value. This ensure * that not present xattr can be distinguished from a 0 * length value or rule that matches any value */ state = aa_dfa_null_transition(attach->xmatch->dfa, state); /* Check xattr value */ state = aa_dfa_match_len(attach->xmatch->dfa, state, value, size); perms = aa_lookup_perms(attach->xmatch, state); if (!(perms->allow & MAY_EXEC)) { ret = -EINVAL; goto out; } } /* transition to next element */ state = aa_dfa_outofband_transition(attach->xmatch->dfa, state); if (size < 0) { /* * No xattr match, so verify if transition to * next element was valid. IFF so the xattr * was optional. */ if (!state) { ret = -EINVAL; goto out; } /* don't count missing optional xattr as matched */ ret--; } } out: kfree(value); return ret;} /** * find_attach - do attachment search for unconfined processes * @path: path of file in question (NOT NULL) * @ns: the current namespace (NOT NULL) * @head: profile list to walk (NOT NULL) * @name: to match against (NOT NULL) * @info: info message if there was an error (NOT NULL) * * Do a linear search on the profiles in the list. There is a matching * preference where an exact match is preferred over a name which uses * expressions to match, and matching expressions with the greatest * xmatch_len are preferred. * * Requires: @head not be shared or have appropriate locks held * * Returns: label or NULL if no match found */static struct aa_label *find_attach(const struct path *path, struct aa_ns *ns, struct list_head *head, const char *name, const char **info){ int candidate_len = 0, candidate_xattrs = 0; bool conflict = false; struct aa_profile *profile, *candidate = NULL; AA_BUG(!path); AA_BUG(!name); AA_BUG(!head); rcu_read_lock();restart: list_for_each_entry_rcu(profile, head, base.list) { struct aa_attachment *attach = &profile->attach; if (profile->label.flags & FLAG_NULL && &profile->label == ns_unconfined(profile->ns)) continue; /* Find the "best" matching profile. Profiles must * match the path and extended attributes (if any) * associated with the file. A more specific path * match will be preferred over a less specific one, * and a match with more matching extended attributes * will be preferred over one with fewer. If the best * match has both the same level of path specificity * and the same number of matching extended attributes * as another profile, signal a conflict and refuse to * match. */ if (attach->xmatch->dfa) { unsigned int count; aa_state_t state; struct aa_perms *perms; state = aa_dfa_leftmatch(attach->xmatch->dfa, attach->xmatch->start[AA_CLASS_XMATCH], name, &count); perms = aa_lookup_perms(attach->xmatch, state); /* any accepting state means a valid match. */ if (perms->allow & MAY_EXEC) { int ret = 0; if (count < candidate_len) continue; if (attach->xattr_count) { long rev = READ_ONCE(ns->revision); if (!aa_get_profile_not0(profile)) goto restart; rcu_read_unlock(); ret = aa_xattrs_match(path, profile, state); rcu_read_lock(); aa_put_profile(profile); if (rev != READ_ONCE(ns->revision)) /* policy changed */ goto restart; /* * Fail matching if the xattrs don't * match */ if (ret < 0) continue; } /* * TODO: allow for more flexible best match * * The new match isn't more specific * than the current best match */ if (count == candidate_len && ret <= candidate_xattrs) { /* Match is equivalent, so conflict */ if (ret == candidate_xattrs) conflict = true; continue; } /* Either the same length with more matching * xattrs, or a longer match */ candidate = profile; candidate_len = max(count, attach->xmatch_len); candidate_xattrs = ret; conflict = false; } } else if (!strcmp(profile->base.name, name)) { /* * old exact non-re match, without conditionals such * as xattrs. no more searching required */ candidate = profile; goto out; } } if (!candidate || conflict) { if (conflict) *info = CONFLICTING_ATTACH_STR; rcu_read_unlock(); return NULL; }Lines 1–500 of 1663. Load more
// 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/mutex.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/lib.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(const struct aa_profile *profile, const 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 * @inview: whether to match labels in view or only in scope * @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(const struct aa_profile *profile, struct aa_label *label, bool stack, aa_state_t state, bool inview, 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 in view and going to be interacted with */ label_for_each(i, label, tp) { if (!aa_ns_visible(profile->ns, tp->ns, inview)) 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, inview)) 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 * @inview: whether to match labels in view or only in scope * @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(const struct aa_profile *profile, struct aa_label *label, bool stack, aa_state_t start, bool inview, 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_each(i, label, tp) { if (!aa_ns_visible(profile->ns, tp->ns, inview)) continue; state = match_component(profile, tp, stack, start); if (!state) goto fail; goto next; } /* no subcomponents visible - no change in perms */ return 0; next: tmp = *(aa_lookup_condperms(current_fsuid(), rules->file, state, &cond)); 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, inview)) continue; state = match_component(profile, tp, stack, start); if (!state) goto fail; tmp = *(aa_lookup_condperms(current_fsuid(), rules->file, state, &cond)); aa_apply_modes_to_perms(profile, &tmp); aa_perms_accum(perms, &tmp); } if ((perms->allow & request) != request) return -EACCES; return 0; fail: *perms = nullperms; return -EACCES; } /** * label_match - do a multi-component label match * @profile: profile to match against (NOT NULL) * @label: label to match (NOT NULL) * @stack: whether this is a stacking request * @state: state to start in * @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(const struct aa_profile *profile, struct aa_label *label, 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, inview, request, perms); if (!error) return error; *perms = allperms; return label_components_match(profile, label, stack, state, inview, request, perms); } /******* end TODO: dedup *****/ /** * change_profile_perms - find permissions for change_profile * @profile: the current profile (NOT NULL) * @target: label to transition to (NOT NULL) * @stack: whether this is a stacking request * @request: requested perms * @start: state to start matching in * @perms: Returns computed perms (NOT NULL) * * * Returns: permission set * * currently only matches full label A//&B//&C or individual components A, B, C * not arbitrary combinations. Eg. A//&B, C */ static int change_profile_perms(const struct aa_profile *profile, struct aa_label *target, bool stack, u32 request, aa_state_t start, struct aa_perms *perms) { if (profile_unconfined(profile)) { perms->allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC; perms->audit = perms->quiet = perms->kill = 0; return 0; } /* TODO: add profile in ns screening */ return label_match(profile, target, stack, start, true, request, perms); } /** * aa_xattrs_match - check whether a file matches the xattrs defined in profile * @path: path for file being matched (NOT NULL) * @profile: profile to match against (NOT NULL) * @state: state to start match in * * Returns: number of extended attributes that matched, or < 0 on error */ static int aa_xattrs_match(const struct path *path, const struct aa_profile *profile, aa_state_t state) { AA_BUG(!path); AA_BUG(!profile); int i; struct dentry *d; char *value = NULL; const struct aa_attachment *attach = &profile->attach; int size, value_size = 0, ret = attach->xattr_count; if (!attach->xattr_count) return 0; might_sleep(); /* transition from exec match to xattr set */ state = aa_dfa_outofband_transition(attach->xmatch->dfa, state); d = path->dentry; for (i = 0; i < attach->xattr_count; i++) { size = vfs_getxattr_alloc(&nop_mnt_idmap, d, attach->xattrs[i], &value, value_size, GFP_KERNEL); if (size >= 0) { struct aa_perms *perms; /* * Check the xattr presence before value. This ensure * that not present xattr can be distinguished from a 0 * length value or rule that matches any value */ state = aa_dfa_null_transition(attach->xmatch->dfa, state); /* Check xattr value */ state = aa_dfa_match_len(attach->xmatch->dfa, state, value, size); perms = aa_lookup_perms(attach->xmatch, state); if (!(perms->allow & MAY_EXEC)) { ret = -EINVAL; goto out; } } /* transition to next element */ state = aa_dfa_outofband_transition(attach->xmatch->dfa, state); if (size < 0) { /* * No xattr match, so verify if transition to * next element was valid. IFF so the xattr * was optional. */ if (!state) { ret = -EINVAL; goto out; } /* don't count missing optional xattr as matched */ ret--; } } out: kfree(value); return ret; } /** * find_attach - do attachment search for unconfined processes * @path: path of file in question (NOT NULL) * @ns: the current namespace (NOT NULL) * @head: profile list to walk (NOT NULL) * @name: to match against (NOT NULL) * @info: info message if there was an error (NOT NULL) * * Do a linear search on the profiles in the list. There is a matching * preference where an exact match is preferred over a name which uses * expressions to match, and matching expressions with the greatest * xmatch_len are preferred. * * Requires: @head not be shared or have appropriate locks held * * Returns: label or NULL if no match found */ static struct aa_label *find_attach(const struct path *path, struct aa_ns *ns, struct list_head *head, const char *name, const char **info) { int candidate_len = 0, candidate_xattrs = 0; bool conflict = false; struct aa_profile *profile, *candidate = NULL; AA_BUG(!path); AA_BUG(!name); AA_BUG(!head); rcu_read_lock(); restart: list_for_each_entry_rcu(profile, head, base.list) { struct aa_attachment *attach = &profile->attach; if (profile->label.flags & FLAG_NULL && &profile->label == ns_unconfined(profile->ns)) continue; /* Find the "best" matching profile. Profiles must * match the path and extended attributes (if any) * associated with the file. A more specific path * match will be preferred over a less specific one, * and a match with more matching extended attributes * will be preferred over one with fewer. If the best * match has both the same level of path specificity * and the same number of matching extended attributes * as another profile, signal a conflict and refuse to * match. */ if (attach->xmatch->dfa) { unsigned int count; aa_state_t state; struct aa_perms *perms; state = aa_dfa_leftmatch(attach->xmatch->dfa, attach->xmatch->start[AA_CLASS_XMATCH], name, &count); perms = aa_lookup_perms(attach->xmatch, state); /* any accepting state means a valid match. */ if (perms->allow & MAY_EXEC) { int ret = 0; if (count < candidate_len) continue; if (attach->xattr_count) { long rev = READ_ONCE(ns->revision); if (!aa_get_profile_not0(profile)) goto restart; rcu_read_unlock(); ret = aa_xattrs_match(path, profile, state); rcu_read_lock(); aa_put_profile(profile); if (rev != READ_ONCE(ns->revision)) /* policy changed */ goto restart; /* * Fail matching if the xattrs don't * match */ if (ret < 0) continue; } /* * TODO: allow for more flexible best match * * The new match isn't more specific * than the current best match */ if (count == candidate_len && ret <= candidate_xattrs) { /* Match is equivalent, so conflict */ if (ret == candidate_xattrs) conflict = true; continue; } /* Either the same length with more matching * xattrs, or a longer match */ candidate = profile; candidate_len = max(count, attach->xmatch_len); candidate_xattrs = ret; conflict = false; } } else if (!strcmp(profile->base.name, name)) { /* * old exact non-re match, without conditionals such * as xattrs. no more searching required */ candidate = profile; goto out; } } if (!candidate || conflict) { if (conflict) *info = CONFLICTING_ATTACH_STR; rcu_read_unlock(); return NULL; }