@@ -88,20 +88,34 @@ struct selinux_avc {
struct avc_cache avc_cache;
};
-static struct selinux_avc selinux_avc;
-
-void selinux_avc_init(struct selinux_avc **avc)
+int selinux_avc_create(struct selinux_avc **avc)
{
+ struct selinux_avc *newavc;
int i;
- selinux_avc.avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD;
+ newavc = kzalloc(sizeof(*newavc), GFP_KERNEL);
+ if (!newavc)
+ return -ENOMEM;
+
+ newavc->avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD;
+
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
- INIT_HLIST_HEAD(&selinux_avc.avc_cache.slots[i]);
- spin_lock_init(&selinux_avc.avc_cache.slots_lock[i]);
+ INIT_HLIST_HEAD(&newavc->avc_cache.slots[i]);
+ spin_lock_init(&newavc->avc_cache.slots_lock[i]);
}
- atomic_set(&selinux_avc.avc_cache.active_nodes, 0);
- atomic_set(&selinux_avc.avc_cache.lru_hint, 0);
- *avc = &selinux_avc;
+ atomic_set(&newavc->avc_cache.active_nodes, 0);
+ atomic_set(&newavc->avc_cache.lru_hint, 0);
+
+ *avc = newavc;
+ return 0;
+}
+
+static void avc_flush(struct selinux_avc *avc);
+
+void selinux_avc_free(struct selinux_avc *avc)
+{
+ avc_flush(avc);
+ kfree(avc);
}
unsigned int avc_get_cache_threshold(struct selinux_avc *avc)
@@ -103,7 +103,6 @@
#include "audit.h"
#include "avc_ss.h"
-static struct selinux_ns init_selinux_ns;
struct selinux_ns *current_selinux_ns;
/* SECMARK reference count */
@@ -7045,15 +7044,70 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
#endif
};
+static void selinux_ns_free(struct work_struct *work);
+
+int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns **ns)
+{
+ struct selinux_ns *newns;
+ int rc;
+
+ newns = kzalloc(sizeof(*newns), GFP_KERNEL);
+ if (!newns)
+ return -ENOMEM;
+
+ refcount_set(&newns->count, 1);
+ INIT_WORK(&newns->work, selinux_ns_free);
+
+ rc = selinux_ss_create(&newns->ss);
+ if (rc)
+ goto err;
+
+ rc = selinux_avc_create(&newns->avc);
+ if (rc)
+ goto err;
+
+ if (parent)
+ newns->parent = get_selinux_ns(parent);
+
+ *ns = newns;
+ return 0;
+err:
+ selinux_ss_free(newns->ss);
+ kfree(newns);
+ return rc;
+}
+
+static void selinux_ns_free(struct work_struct *work)
+{
+ struct selinux_ns *parent, *ns =
+ container_of(work, struct selinux_ns, work);
+
+ do {
+ parent = ns->parent;
+ selinux_ss_free(ns->ss);
+ selinux_avc_free(ns->avc);
+ kfree(ns);
+ ns = parent;
+ } while (ns && refcount_dec_and_test(&ns->count));
+}
+
+void __put_selinux_ns(struct selinux_ns *ns)
+{
+ schedule_work(&ns->work);
+}
+
+static struct selinux_ns *init_selinux_ns;
+
static __init int selinux_init(void)
{
pr_info("SELinux: Initializing.\n");
- enforcing_set(&init_selinux_ns, selinux_enforcing_boot);
- init_selinux_ns.checkreqprot = selinux_checkreqprot_boot;
- selinux_ss_init(&init_selinux_ns.ss);
- selinux_avc_init(&init_selinux_ns.avc);
- current_selinux_ns = &init_selinux_ns;
+ if (selinux_ns_create(NULL, &init_selinux_ns))
+ panic("SELinux: Could not create initial namespace\n");
+
+ enforcing_set(init_selinux_ns, selinux_enforcing_boot);
+ init_selinux_ns->checkreqprot = selinux_checkreqprot_boot;
+ current_selinux_ns = init_selinux_ns;
/* Set the security state for the initial task. */
cred_init_security();
@@ -7226,7 +7280,7 @@ int selinux_disable(struct selinux_ns *ns)
* within the ns will interpret the absence of a selinuxfs mount
* as SELinux being disabled.
*/
- if (ns != &init_selinux_ns)
+ if (ns != init_selinux_ns)
return 0;
pr_info("SELinux: Disabled at runtime.\n");
@@ -99,6 +99,8 @@ struct selinux_avc;
struct selinux_ss;
struct selinux_ns {
+ refcount_t count;
+ struct work_struct work;
bool disabled;
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
bool enforcing;
@@ -108,10 +110,29 @@ struct selinux_ns {
bool policycap[__POLICYDB_CAPABILITY_MAX];
struct selinux_avc *avc;
struct selinux_ss *ss;
+ struct selinux_ns *parent;
};
-void selinux_ss_init(struct selinux_ss **ss);
-void selinux_avc_init(struct selinux_avc **avc);
+int selinux_ns_create(struct selinux_ns *parent, struct selinux_ns **ns);
+void __put_selinux_ns(struct selinux_ns *ns);
+
+int selinux_ss_create(struct selinux_ss **ss);
+void selinux_ss_free(struct selinux_ss *ss);
+
+int selinux_avc_create(struct selinux_avc **avc);
+void selinux_avc_free(struct selinux_avc *avc);
+
+static inline void put_selinux_ns(struct selinux_ns *ns)
+{
+ if (ns && refcount_dec_and_test(&ns->count))
+ __put_selinux_ns(ns);
+}
+
+static inline struct selinux_ns *get_selinux_ns(struct selinux_ns *ns)
+{
+ refcount_inc(&ns->count);
+ return ns;
+}
extern struct selinux_ns *current_selinux_ns;
@@ -90,7 +90,7 @@ static int selinux_fs_info_create(struct super_block *sb)
mutex_init(&fsi->mutex);
fsi->last_ino = SEL_INO_NEXT - 1;
- fsi->ns = current_selinux_ns;
+ fsi->ns = get_selinux_ns(current_selinux_ns);
fsi->sb = sb;
sb->s_fs_info = fsi;
return 0;
@@ -102,6 +102,7 @@ static void selinux_fs_info_free(struct super_block *sb)
int i;
if (fsi) {
+ put_selinux_ns(fsi->ns);
for (i = 0; i < fsi->bool_num; i++)
kfree(fsi->bool_pending_names[i]);
kfree(fsi->bool_pending_names);
@@ -76,13 +76,28 @@ const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
"nnp_nosuid_transition"
};
-static struct selinux_ss selinux_ss;
+int selinux_ss_create(struct selinux_ss **ss)
+{
+ struct selinux_ss *newss;
+
+ newss = kzalloc(sizeof(*newss), GFP_KERNEL);
+ if (!newss)
+ return -ENOMEM;
+ rwlock_init(&newss->policy_rwlock);
+ mutex_init(&newss->status_lock);
+ *ss = newss;
+ return 0;
+}
-void selinux_ss_init(struct selinux_ss **ss)
+void selinux_ss_free(struct selinux_ss *ss)
{
- rwlock_init(&selinux_ss.policy_rwlock);
- mutex_init(&selinux_ss.status_lock);
- *ss = &selinux_ss;
+ if (ss->sidtab)
+ sidtab_destroy(ss->sidtab);
+ policydb_destroy(&ss->policydb);
+ kfree(ss->map.mapping);
+ if (ss->status_page)
+ __free_page(ss->status_page);
+ kfree(ss);
}
/* Forward declaration. */
Move from static allocation of a single selinux namespace to dynamic allocation. Include necessary support for lifecycle management of the selinux namespace, modeled after the user namespace support. Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov> --- security/selinux/avc.c | 32 ++++++++++---- security/selinux/hooks.c | 68 ++++++++++++++++++++++++++--- security/selinux/include/security.h | 25 ++++++++++- security/selinux/selinuxfs.c | 3 +- security/selinux/ss/services.c | 25 ++++++++--- 5 files changed, 129 insertions(+), 24 deletions(-)