// SPDX-License-Identifier: GPL-2.0-only/* * AppArmor security module * * This file contains AppArmor policy manipulation functions * * Copyright (C) 1998-2008 Novell/SUSE * Copyright 2009-2010 Canonical Ltd. * * AppArmor policy is based around profiles, which contain the rules a * task is confined by. Every task in the system has a profile attached * to it determined either by matching "unconfined" tasks against the * visible set of profiles or by following a profiles attachment rules. * * Each profile exists in a profile namespace which is a container of * visible profiles. Each namespace contains a special "unconfined" profile, * which doesn't enforce any confinement on a task beyond DAC. * * Namespace and profile names can be written together in either * of two syntaxes. * :namespace:profile - used by kernel interfaces for easy detection * namespace://profile - used by policy * * Profile names can not start with : or @ or ^ and may not contain \0 * * Reserved profile names * unconfined - special automatically generated unconfined profile * inherit - special name to indicate profile inheritance * null-XXXX-YYYY - special automatically generated learning profiles * * Namespace names may not start with / or @ and may not contain \0 or : * Reserved namespace names * user-XXXX - user defined profiles * * a // in a profile or namespace name indicates a hierarchical name with the * name before the // being the parent and the name after the child. * * Profile and namespace hierarchies serve two different but similar purposes. * The namespace contains the set of visible profiles that are considered * for attachment. The hierarchy of namespaces allows for virtualizing * the namespace so that for example a chroot can have its own set of profiles * which may define some local user namespaces. * The profile hierarchy severs two distinct purposes, * - it allows for sub profiles or hats, which allows an application to run * subprograms under its own profile with different restriction than it * self, and not have it use the system profile. * eg. if a mail program starts an editor, the policy might make the * restrictions tighter on the editor tighter than the mail program, * and definitely different than general editor restrictions * - it allows for binary hierarchy of profiles, so that execution history * is preserved. This feature isn't exploited by AppArmor reference policy * but is allowed. NOTE: this is currently suboptimal because profile * aliasing is not currently implemented so that a profile for each * level must be defined. * eg. /bin/bash///bin/ls as a name would indicate /bin/ls was started * from /bin/bash * * A profile or namespace name that can contain one or more // separators * is referred to as an hname (hierarchical). * eg. /bin/bash//bin/ls * * An fqname is a name that may contain both namespace and profile hnames. * eg. :ns:/bin/bash//bin/ls * * NOTES: * - locking of profile lists is currently fairly coarse. All profile * lists within a namespace use the namespace lock. * FIXME: move profile lists to using rcu_lists */ #include <linux/slab.h>#include <linux/spinlock.h>#include <linux/string.h>#include <linux/cred.h>#include <linux/rculist.h>#include <linux/user_namespace.h> #include "include/apparmor.h"#include "include/capability.h"#include "include/cred.h"#include "include/file.h"#include "include/ipc.h"#include "include/match.h"#include "include/net.h"#include "include/path.h"#include "include/policy.h"#include "include/policy_ns.h"#include "include/policy_unpack.h"#include "include/resource.h" int unprivileged_userns_apparmor_policy = 1;int aa_unprivileged_unconfined_restricted; const char *const aa_profile_mode_names[] = { "enforce", "complain", "kill", "unconfined", "user",}; void aa_destroy_tags(struct aa_tags_struct *tags){ kfree_sensitive(tags->hdrs.table); kfree_sensitive(tags->sets.table); aa_destroy_str_table(&tags->strs); memset(tags, 0, sizeof(*tags));} static void aa_free_pdb(struct aa_policydb *pdb){ if (pdb) { aa_put_dfa(pdb->dfa); kvfree(pdb->perms); aa_destroy_str_table(&pdb->trans); aa_destroy_tags(&pdb->tags); kfree(pdb); }} /** * aa_pdb_free_kref - free aa_policydb by kref (called by aa_put_pdb) * @kref: kref callback for freeing of a dfa (NOT NULL) */void aa_pdb_free_kref(struct kref *kref){ struct aa_policydb *pdb = container_of(kref, struct aa_policydb, count); aa_free_pdb(pdb);} struct aa_policydb *aa_alloc_pdb(gfp_t gfp){ struct aa_policydb *pdb = kzalloc_obj(struct aa_policydb, gfp); if (!pdb) return NULL; kref_init(&pdb->count); return pdb;} /** * __add_profile - add a profiles to list and label tree * @list: list to add it to (NOT NULL) * @profile: the profile to add (NOT NULL) * * refcount @profile, should be put by __list_remove_profile * * Requires: namespace lock be held, or list not be shared */static void __add_profile(struct list_head *list, struct aa_profile *profile){ struct aa_label *l; AA_BUG(!list); AA_BUG(!profile); AA_BUG(!profile->ns); AA_BUG(!mutex_is_locked(&profile->ns->lock)); list_add_rcu(&profile->base.list, list); /* get list reference */ aa_get_profile(profile); l = aa_label_insert(&profile->ns->labels, &profile->label); AA_BUG(l != &profile->label); aa_put_label(l);} /** * __list_remove_profile - remove a profile from the list it is on * @profile: the profile to remove (NOT NULL) * * remove a profile from the list, warning generally removal should * be done with __replace_profile as most profile removals are * replacements to the unconfined profile. * * put @profile list refcount * * Requires: namespace lock be held, or list not have been live */static void __list_remove_profile(struct aa_profile *profile){ AA_BUG(!profile); AA_BUG(!profile->ns); AA_BUG(!mutex_is_locked(&profile->ns->lock)); list_del_rcu(&profile->base.list); aa_put_profile(profile);} /** * __remove_profile - remove profile, and children * @profile: profile to be removed (NOT NULL) * * Requires: namespace list lock be held, or list not be shared */static void __remove_profile(struct aa_profile *profile){ struct aa_profile *curr, *to_remove; AA_BUG(!profile); AA_BUG(!profile->ns); AA_BUG(!mutex_is_locked(&profile->ns->lock)); /* release any children lists first */ if (!list_empty(&profile->base.profiles)) { curr = list_first_entry(&profile->base.profiles, struct aa_profile, base.list); while (curr != profile) { while (!list_empty(&curr->base.profiles)) curr = list_first_entry(&curr->base.profiles, struct aa_profile, base.list); to_remove = curr; if (!list_is_last(&to_remove->base.list, &aa_deref_parent(curr)->base.profiles)) curr = list_next_entry(to_remove, base.list); else curr = aa_deref_parent(curr); /* released by free_profile */ aa_label_remove(&to_remove->label); __aafs_profile_rmdir(to_remove); __list_remove_profile(to_remove); } } /* released by free_profile */ aa_label_remove(&profile->label); __aafs_profile_rmdir(profile); __list_remove_profile(profile); /* rawdata is only ever referenced by fs lookup, that is no * longer possible here, so put the reference to it. This will * enable the rawdata to be freed if for some reason the profile * is pinned and going to live for a while. */ aa_put_profile_loaddata(profile->rawdata); profile->rawdata = NULL;} /** * __aa_profile_list_release - remove all profiles on the list and put refs * @head: list of profiles (NOT NULL) * * Requires: namespace lock be held */void __aa_profile_list_release(struct list_head *head){ struct aa_profile *profile, *tmp; list_for_each_entry_safe(profile, tmp, head, base.list) __remove_profile(profile);} /** * aa_free_data - free a data blob * @ptr: data to free * @arg: unused */static void aa_free_data(void *ptr, void *arg){ struct aa_data *data = ptr; if (!ptr) return; kvfree_sensitive(data->data, data->size); kfree_sensitive(data->key); kfree_sensitive(data);} static void free_attachment(struct aa_attachment *attach){ int i; if (!attach) return; for (i = 0; i < attach->xattr_count; i++) kfree_sensitive(attach->xattrs[i]); kfree_sensitive(attach->xattrs); aa_put_pdb(attach->xmatch);} static void free_ruleset(struct aa_ruleset *rules){ int i; if (!rules) return; aa_put_pdb(rules->file); aa_put_pdb(rules->policy); aa_free_cap_rules(&rules->caps); aa_free_rlimit_rules(&rules->rlimits); for (i = 0; i < rules->secmark_count; i++) kfree_sensitive(rules->secmark[i].label); kfree_sensitive(rules->secmark); kfree_sensitive(rules);} struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp){ struct aa_ruleset *rules; rules = kzalloc_obj(*rules, gfp); return rules;} /** * aa_free_profile - free a profile * @profile: the profile to free (MAYBE NULL) * * Free a profile, its hats and null_profile. All references to the profile, * its hats and null_profile must have been put. * * If the profile was referenced from a task context, free_profile() will * be called from an rcu callback routine, so we must not sleep here. */void aa_free_profile(struct aa_profile *profile){ struct rhashtable *rht; AA_DEBUG(DEBUG_POLICY, "%s(%p)\n", __func__, profile); if (!profile) return; /* free children profiles */ aa_policy_destroy(&profile->base); aa_put_profile(rcu_access_pointer(profile->parent)); aa_put_ns(profile->ns); kfree_sensitive(profile->rename); kfree_sensitive(profile->disconnected); free_attachment(&profile->attach); /* * at this point there are no tasks that can have a reference * to rules */ for (int i = 0; i < profile->n_rules; i++) free_ruleset(profile->label.rules[i]); kfree_sensitive(profile->dirname); if (profile->data) { rht = profile->data; profile->data = NULL; rhashtable_free_and_destroy(rht, aa_free_data, NULL); kfree_sensitive(rht); } kfree_sensitive(profile->hash); aa_put_profile_loaddata(profile->rawdata); aa_label_destroy(&profile->label); kfree_sensitive(profile);} /** * aa_alloc_profile - allocate, initialize and return a new profile * @hname: name of the profile (NOT NULL) * @proxy: proxy to use OR null if to allocate a new one * @gfp: allocation type * * Returns: refcount profile or NULL on failure */struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, gfp_t gfp){ struct aa_profile *profile; /* freed by free_profile - usually through aa_put_profile * this adds space for a single ruleset in the rules section of the * label */ profile = kzalloc_flex(*profile, label.rules, 1, gfp); if (!profile) return NULL; if (!aa_policy_init(&profile->base, NULL, hname, gfp)) goto fail; if (!aa_label_init(&profile->label, 1, gfp)) goto fail; /* allocate the first ruleset, but leave it empty */ profile->label.rules[0] = aa_alloc_ruleset(gfp); if (!profile->label.rules[0]) goto fail; profile->n_rules = 1; /* update being set needed by fs interface */ if (!proxy) { proxy = aa_alloc_proxy(&profile->label, gfp); if (!proxy) goto fail; } else aa_get_proxy(proxy); profile->label.proxy = proxy; profile->label.hname = profile->base.hname; profile->label.flags |= FLAG_PROFILE; profile->label.vec[0] = profile; profile->signal = SIGKILL; /* refcount released by caller */ return profile; fail: aa_free_profile(profile); return NULL;} static inline bool ANY_RULE_MEDIATES(struct aa_profile *profile, unsigned char class){ int i; for (i = 0; i < profile->n_rules; i++) { if (RULE_MEDIATES(profile->label.rules[i], class)) return true; } return false;} /* set of rules that are mediated by unconfined */static int unconfined_mediates[] = { AA_CLASS_NS, AA_CLASS_IO_URING, 0 }; /* must be called after profile rulesets and start information is setup */void aa_compute_profile_mediates(struct aa_profile *profile){ int c; if (profile_unconfined(profile)) { int *pos; for (pos = unconfined_mediates; *pos; pos++) { if (ANY_RULE_MEDIATES(profile, *pos)) profile->label.mediates |= ((u64) 1) << AA_CLASS_NS; } return; } for (c = 0; c <= AA_CLASS_LAST; c++) { if (ANY_RULE_MEDIATES(profile, c)) profile->label.mediates |= ((u64) 1) << c; }} /* TODO: profile accounting - setup in remove */ /** * __strn_find_child - find a profile on @head list using substring of @name * @head: list to search (NOT NULL) * @name: name of profile (NOT NULL) * @len: length of @name substring to match * * Requires: rcu_read_lock be held * * Returns: unrefcounted profile ptr, or NULL if not found */static struct aa_profile *__strn_find_child(struct list_head *head, const char *name, int len){ return (struct aa_profile *)__policy_strn_find(head, name, len);} /** * __find_child - find a profile on @head list with a name matching @name * @head: list to search (NOT NULL) * @name: name of profile (NOT NULL) * * Requires: rcu_read_lock be held * * Returns: unrefcounted profile ptr, or NULL if not found */static struct aa_profile *__find_child(struct list_head *head, const char *name){ return __strn_find_child(head, name, strlen(name));} /** * aa_find_child - find a profile by @name in @parent * @parent: profile to search (NOT NULL) * @name: profile name to search for (NOT NULL) * * Returns: a refcounted profile or NULL if not found */struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name){ struct aa_profile *profile; rcu_read_lock();Lines 1–500 of 1537. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * AppArmor security module * * This file contains AppArmor policy manipulation functions * * Copyright (C) 1998-2008 Novell/SUSE * Copyright 2009-2010 Canonical Ltd. * * AppArmor policy is based around profiles, which contain the rules a * task is confined by. Every task in the system has a profile attached * to it determined either by matching "unconfined" tasks against the * visible set of profiles or by following a profiles attachment rules. * * Each profile exists in a profile namespace which is a container of * visible profiles. Each namespace contains a special "unconfined" profile, * which doesn't enforce any confinement on a task beyond DAC. * * Namespace and profile names can be written together in either * of two syntaxes. * :namespace:profile - used by kernel interfaces for easy detection * namespace://profile - used by policy * * Profile names can not start with : or @ or ^ and may not contain \0 * * Reserved profile names * unconfined - special automatically generated unconfined profile * inherit - special name to indicate profile inheritance * null-XXXX-YYYY - special automatically generated learning profiles * * Namespace names may not start with / or @ and may not contain \0 or : * Reserved namespace names * user-XXXX - user defined profiles * * a // in a profile or namespace name indicates a hierarchical name with the * name before the // being the parent and the name after the child. * * Profile and namespace hierarchies serve two different but similar purposes. * The namespace contains the set of visible profiles that are considered * for attachment. The hierarchy of namespaces allows for virtualizing * the namespace so that for example a chroot can have its own set of profiles * which may define some local user namespaces. * The profile hierarchy severs two distinct purposes, * - it allows for sub profiles or hats, which allows an application to run * subprograms under its own profile with different restriction than it * self, and not have it use the system profile. * eg. if a mail program starts an editor, the policy might make the * restrictions tighter on the editor tighter than the mail program, * and definitely different than general editor restrictions * - it allows for binary hierarchy of profiles, so that execution history * is preserved. This feature isn't exploited by AppArmor reference policy * but is allowed. NOTE: this is currently suboptimal because profile * aliasing is not currently implemented so that a profile for each * level must be defined. * eg. /bin/bash///bin/ls as a name would indicate /bin/ls was started * from /bin/bash * * A profile or namespace name that can contain one or more // separators * is referred to as an hname (hierarchical). * eg. /bin/bash//bin/ls * * An fqname is a name that may contain both namespace and profile hnames. * eg. :ns:/bin/bash//bin/ls * * NOTES: * - locking of profile lists is currently fairly coarse. All profile * lists within a namespace use the namespace lock. * FIXME: move profile lists to using rcu_lists */ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/string.h> #include <linux/cred.h> #include <linux/rculist.h> #include <linux/user_namespace.h> #include "include/apparmor.h" #include "include/capability.h" #include "include/cred.h" #include "include/file.h" #include "include/ipc.h" #include "include/match.h" #include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/policy_ns.h" #include "include/policy_unpack.h" #include "include/resource.h" int unprivileged_userns_apparmor_policy = 1; int aa_unprivileged_unconfined_restricted; const char *const aa_profile_mode_names[] = { "enforce", "complain", "kill", "unconfined", "user", }; void aa_destroy_tags(struct aa_tags_struct *tags) { kfree_sensitive(tags->hdrs.table); kfree_sensitive(tags->sets.table); aa_destroy_str_table(&tags->strs); memset(tags, 0, sizeof(*tags)); } static void aa_free_pdb(struct aa_policydb *pdb) { if (pdb) { aa_put_dfa(pdb->dfa); kvfree(pdb->perms); aa_destroy_str_table(&pdb->trans); aa_destroy_tags(&pdb->tags); kfree(pdb); } } /** * aa_pdb_free_kref - free aa_policydb by kref (called by aa_put_pdb) * @kref: kref callback for freeing of a dfa (NOT NULL) */ void aa_pdb_free_kref(struct kref *kref) { struct aa_policydb *pdb = container_of(kref, struct aa_policydb, count); aa_free_pdb(pdb); } struct aa_policydb *aa_alloc_pdb(gfp_t gfp) { struct aa_policydb *pdb = kzalloc_obj(struct aa_policydb, gfp); if (!pdb) return NULL; kref_init(&pdb->count); return pdb; } /** * __add_profile - add a profiles to list and label tree * @list: list to add it to (NOT NULL) * @profile: the profile to add (NOT NULL) * * refcount @profile, should be put by __list_remove_profile * * Requires: namespace lock be held, or list not be shared */ static void __add_profile(struct list_head *list, struct aa_profile *profile) { struct aa_label *l; AA_BUG(!list); AA_BUG(!profile); AA_BUG(!profile->ns); AA_BUG(!mutex_is_locked(&profile->ns->lock)); list_add_rcu(&profile->base.list, list); /* get list reference */ aa_get_profile(profile); l = aa_label_insert(&profile->ns->labels, &profile->label); AA_BUG(l != &profile->label); aa_put_label(l); } /** * __list_remove_profile - remove a profile from the list it is on * @profile: the profile to remove (NOT NULL) * * remove a profile from the list, warning generally removal should * be done with __replace_profile as most profile removals are * replacements to the unconfined profile. * * put @profile list refcount * * Requires: namespace lock be held, or list not have been live */ static void __list_remove_profile(struct aa_profile *profile) { AA_BUG(!profile); AA_BUG(!profile->ns); AA_BUG(!mutex_is_locked(&profile->ns->lock)); list_del_rcu(&profile->base.list); aa_put_profile(profile); } /** * __remove_profile - remove profile, and children * @profile: profile to be removed (NOT NULL) * * Requires: namespace list lock be held, or list not be shared */ static void __remove_profile(struct aa_profile *profile) { struct aa_profile *curr, *to_remove; AA_BUG(!profile); AA_BUG(!profile->ns); AA_BUG(!mutex_is_locked(&profile->ns->lock)); /* release any children lists first */ if (!list_empty(&profile->base.profiles)) { curr = list_first_entry(&profile->base.profiles, struct aa_profile, base.list); while (curr != profile) { while (!list_empty(&curr->base.profiles)) curr = list_first_entry(&curr->base.profiles, struct aa_profile, base.list); to_remove = curr; if (!list_is_last(&to_remove->base.list, &aa_deref_parent(curr)->base.profiles)) curr = list_next_entry(to_remove, base.list); else curr = aa_deref_parent(curr); /* released by free_profile */ aa_label_remove(&to_remove->label); __aafs_profile_rmdir(to_remove); __list_remove_profile(to_remove); } } /* released by free_profile */ aa_label_remove(&profile->label); __aafs_profile_rmdir(profile); __list_remove_profile(profile); /* rawdata is only ever referenced by fs lookup, that is no * longer possible here, so put the reference to it. This will * enable the rawdata to be freed if for some reason the profile * is pinned and going to live for a while. */ aa_put_profile_loaddata(profile->rawdata); profile->rawdata = NULL; } /** * __aa_profile_list_release - remove all profiles on the list and put refs * @head: list of profiles (NOT NULL) * * Requires: namespace lock be held */ void __aa_profile_list_release(struct list_head *head) { struct aa_profile *profile, *tmp; list_for_each_entry_safe(profile, tmp, head, base.list) __remove_profile(profile); } /** * aa_free_data - free a data blob * @ptr: data to free * @arg: unused */ static void aa_free_data(void *ptr, void *arg) { struct aa_data *data = ptr; if (!ptr) return; kvfree_sensitive(data->data, data->size); kfree_sensitive(data->key); kfree_sensitive(data); } static void free_attachment(struct aa_attachment *attach) { int i; if (!attach) return; for (i = 0; i < attach->xattr_count; i++) kfree_sensitive(attach->xattrs[i]); kfree_sensitive(attach->xattrs); aa_put_pdb(attach->xmatch); } static void free_ruleset(struct aa_ruleset *rules) { int i; if (!rules) return; aa_put_pdb(rules->file); aa_put_pdb(rules->policy); aa_free_cap_rules(&rules->caps); aa_free_rlimit_rules(&rules->rlimits); for (i = 0; i < rules->secmark_count; i++) kfree_sensitive(rules->secmark[i].label); kfree_sensitive(rules->secmark); kfree_sensitive(rules); } struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp) { struct aa_ruleset *rules; rules = kzalloc_obj(*rules, gfp); return rules; } /** * aa_free_profile - free a profile * @profile: the profile to free (MAYBE NULL) * * Free a profile, its hats and null_profile. All references to the profile, * its hats and null_profile must have been put. * * If the profile was referenced from a task context, free_profile() will * be called from an rcu callback routine, so we must not sleep here. */ void aa_free_profile(struct aa_profile *profile) { struct rhashtable *rht; AA_DEBUG(DEBUG_POLICY, "%s(%p)\n", __func__, profile); if (!profile) return; /* free children profiles */ aa_policy_destroy(&profile->base); aa_put_profile(rcu_access_pointer(profile->parent)); aa_put_ns(profile->ns); kfree_sensitive(profile->rename); kfree_sensitive(profile->disconnected); free_attachment(&profile->attach); /* * at this point there are no tasks that can have a reference * to rules */ for (int i = 0; i < profile->n_rules; i++) free_ruleset(profile->label.rules[i]); kfree_sensitive(profile->dirname); if (profile->data) { rht = profile->data; profile->data = NULL; rhashtable_free_and_destroy(rht, aa_free_data, NULL); kfree_sensitive(rht); } kfree_sensitive(profile->hash); aa_put_profile_loaddata(profile->rawdata); aa_label_destroy(&profile->label); kfree_sensitive(profile); } /** * aa_alloc_profile - allocate, initialize and return a new profile * @hname: name of the profile (NOT NULL) * @proxy: proxy to use OR null if to allocate a new one * @gfp: allocation type * * Returns: refcount profile or NULL on failure */ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, gfp_t gfp) { struct aa_profile *profile; /* freed by free_profile - usually through aa_put_profile * this adds space for a single ruleset in the rules section of the * label */ profile = kzalloc_flex(*profile, label.rules, 1, gfp); if (!profile) return NULL; if (!aa_policy_init(&profile->base, NULL, hname, gfp)) goto fail; if (!aa_label_init(&profile->label, 1, gfp)) goto fail; /* allocate the first ruleset, but leave it empty */ profile->label.rules[0] = aa_alloc_ruleset(gfp); if (!profile->label.rules[0]) goto fail; profile->n_rules = 1; /* update being set needed by fs interface */ if (!proxy) { proxy = aa_alloc_proxy(&profile->label, gfp); if (!proxy) goto fail; } else aa_get_proxy(proxy); profile->label.proxy = proxy; profile->label.hname = profile->base.hname; profile->label.flags |= FLAG_PROFILE; profile->label.vec[0] = profile; profile->signal = SIGKILL; /* refcount released by caller */ return profile; fail: aa_free_profile(profile); return NULL; } static inline bool ANY_RULE_MEDIATES(struct aa_profile *profile, unsigned char class) { int i; for (i = 0; i < profile->n_rules; i++) { if (RULE_MEDIATES(profile->label.rules[i], class)) return true; } return false; } /* set of rules that are mediated by unconfined */ static int unconfined_mediates[] = { AA_CLASS_NS, AA_CLASS_IO_URING, 0 }; /* must be called after profile rulesets and start information is setup */ void aa_compute_profile_mediates(struct aa_profile *profile) { int c; if (profile_unconfined(profile)) { int *pos; for (pos = unconfined_mediates; *pos; pos++) { if (ANY_RULE_MEDIATES(profile, *pos)) profile->label.mediates |= ((u64) 1) << AA_CLASS_NS; } return; } for (c = 0; c <= AA_CLASS_LAST; c++) { if (ANY_RULE_MEDIATES(profile, c)) profile->label.mediates |= ((u64) 1) << c; } } /* TODO: profile accounting - setup in remove */ /** * __strn_find_child - find a profile on @head list using substring of @name * @head: list to search (NOT NULL) * @name: name of profile (NOT NULL) * @len: length of @name substring to match * * Requires: rcu_read_lock be held * * Returns: unrefcounted profile ptr, or NULL if not found */ static struct aa_profile *__strn_find_child(struct list_head *head, const char *name, int len) { return (struct aa_profile *)__policy_strn_find(head, name, len); } /** * __find_child - find a profile on @head list with a name matching @name * @head: list to search (NOT NULL) * @name: name of profile (NOT NULL) * * Requires: rcu_read_lock be held * * Returns: unrefcounted profile ptr, or NULL if not found */ static struct aa_profile *__find_child(struct list_head *head, const char *name) { return __strn_find_child(head, name, strlen(name)); } /** * aa_find_child - find a profile by @name in @parent * @parent: profile to search (NOT NULL) * @name: profile name to search for (NOT NULL) * * Returns: a refcounted profile or NULL if not found */ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) { struct aa_profile *profile; rcu_read_lock();