From 6778948f9de86c3cfaf36725a7c87dcff9ba247f Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 11 Dec 2023 08:20:59 +0000 Subject: [PATCH] kernel_5.10 no rt --- kernel/security/selinux/ss/services.c | 1578 +++++++++++++++++++++++++++++++++------------------------ 1 files changed, 911 insertions(+), 667 deletions(-) diff --git a/kernel/security/selinux/ss/services.c b/kernel/security/selinux/ss/services.c index 1349f9e..3d63372 100644 --- a/kernel/security/selinux/ss/services.c +++ b/kernel/security/selinux/ss/services.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Implementation of the security services. * @@ -35,9 +36,6 @@ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2. */ #include <linux/kernel.h> #include <linux/slab.h> @@ -48,9 +46,6 @@ #include <linux/in.h> #include <linux/sched.h> #include <linux/audit.h> -#include <linux/mutex.h> -#include <linux/selinux.h> -#include <linux/flex_array.h> #include <linux/vmalloc.h> #include <net/netlabel.h> @@ -69,31 +64,32 @@ #include "xfrm.h" #include "ebitmap.h" #include "audit.h" +#include "policycap_names.h" -/* Policy capability names */ -const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = { - "network_peer_controls", - "open_perms", - "extended_socket_class", - "always_check_network", - "cgroup_seclabel", - "nnp_nosuid_transition" +#include <trace/hooks/selinux.h> + +struct convert_context_args { + struct selinux_state *state; + struct policydb *oldp; + struct policydb *newp; }; -static struct selinux_ss selinux_ss; - -void selinux_ss_init(struct selinux_ss **ss) -{ - rwlock_init(&selinux_ss.policy_rwlock); - mutex_init(&selinux_ss.status_lock); - *ss = &selinux_ss; -} +struct selinux_policy_convert_data { + struct convert_context_args args; + struct sidtab_convert_params sidtab_params; +}; /* Forward declaration. */ static int context_struct_to_string(struct policydb *policydb, struct context *context, char **scontext, u32 *scontext_len); + +static int sidtab_entry_to_string(struct policydb *policydb, + struct sidtab *sidtab, + struct sidtab_entry *entry, + char **scontext, + u32 *scontext_len); static void context_struct_compute_av(struct policydb *policydb, struct context *scontext, @@ -247,9 +243,17 @@ int security_mls_enabled(struct selinux_state *state) { - struct policydb *p = &state->ss->policydb; + int mls_enabled; + struct selinux_policy *policy; - return p->mls_enabled; + if (!selinux_initialized(state)) + return 0; + + rcu_read_lock(); + policy = rcu_dereference(state->policy); + mls_enabled = policy->policydb.mls_enabled; + rcu_read_unlock(); + return mls_enabled; } /* @@ -481,11 +485,11 @@ /* init permission_names */ if (common_dat && - hashtab_map(common_dat->permissions.table, + hashtab_map(&common_dat->permissions.table, dump_masked_av_helper, permission_names) < 0) goto out; - if (hashtab_map(tclass_dat->permissions.table, + if (hashtab_map(&tclass_dat->permissions.table, dump_masked_av_helper, permission_names) < 0) goto out; @@ -546,15 +550,13 @@ struct type_datum *target; u32 masked = 0; - source = flex_array_get_ptr(policydb->type_val_to_struct_array, - scontext->type - 1); + source = policydb->type_val_to_struct[scontext->type - 1]; BUG_ON(!source); if (!source->bounds) return; - target = flex_array_get_ptr(policydb->type_val_to_struct_array, - tcontext->type - 1); + target = policydb->type_val_to_struct[tcontext->type - 1]; BUG_ON(!target); memset(&lo_avd, 0, sizeof(lo_avd)); @@ -654,12 +656,8 @@ */ avkey.target_class = tclass; avkey.specified = AVTAB_AV | AVTAB_XPERMS; - sattr = flex_array_get(policydb->type_attr_map_array, - scontext->type - 1); - BUG_ON(!sattr); - tattr = flex_array_get(policydb->type_attr_map_array, - tcontext->type - 1); - BUG_ON(!tattr); + sattr = &policydb->type_attr_map_array[scontext->type - 1]; + tattr = &policydb->type_attr_map_array[tcontext->type - 1]; ebitmap_for_each_positive_bit(sattr, snode, i) { ebitmap_for_each_positive_bit(tattr, tnode, j) { avkey.source_type = i + 1; @@ -726,20 +724,22 @@ } static int security_validtrans_handle_fail(struct selinux_state *state, - struct context *ocontext, - struct context *ncontext, - struct context *tcontext, - u16 tclass) + struct selinux_policy *policy, + struct sidtab_entry *oentry, + struct sidtab_entry *nentry, + struct sidtab_entry *tentry, + u16 tclass) { - struct policydb *p = &state->ss->policydb; + struct policydb *p = &policy->policydb; + struct sidtab *sidtab = policy->sidtab; char *o = NULL, *n = NULL, *t = NULL; u32 olen, nlen, tlen; - if (context_struct_to_string(p, ocontext, &o, &olen)) + if (sidtab_entry_to_string(p, sidtab, oentry, &o, &olen)) goto out; - if (context_struct_to_string(p, ncontext, &n, &nlen)) + if (sidtab_entry_to_string(p, sidtab, nentry, &n, &nlen)) goto out; - if (context_struct_to_string(p, tcontext, &t, &tlen)) + if (sidtab_entry_to_string(p, sidtab, tentry, &t, &tlen)) goto out; audit_log(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR, "op=security_validate_transition seresult=denied" @@ -759,27 +759,29 @@ u32 oldsid, u32 newsid, u32 tasksid, u16 orig_tclass, bool user) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; - struct context *ocontext; - struct context *ncontext; - struct context *tcontext; + struct sidtab_entry *oentry; + struct sidtab_entry *nentry; + struct sidtab_entry *tentry; struct class_datum *tclass_datum; struct constraint_node *constraint; u16 tclass; int rc = 0; - if (!state->initialized) + if (!selinux_initialized(state)) return 0; - read_lock(&state->ss->policy_rwlock); + rcu_read_lock(); - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; if (!user) - tclass = unmap_class(&state->ss->map, orig_tclass); + tclass = unmap_class(&policy->map, orig_tclass); else tclass = orig_tclass; @@ -789,24 +791,24 @@ } tclass_datum = policydb->class_val_to_struct[tclass - 1]; - ocontext = sidtab_search(sidtab, oldsid); - if (!ocontext) { + oentry = sidtab_search_entry(sidtab, oldsid); + if (!oentry) { pr_err("SELinux: %s: unrecognized SID %d\n", __func__, oldsid); rc = -EINVAL; goto out; } - ncontext = sidtab_search(sidtab, newsid); - if (!ncontext) { + nentry = sidtab_search_entry(sidtab, newsid); + if (!nentry) { pr_err("SELinux: %s: unrecognized SID %d\n", __func__, newsid); rc = -EINVAL; goto out; } - tcontext = sidtab_search(sidtab, tasksid); - if (!tcontext) { + tentry = sidtab_search_entry(sidtab, tasksid); + if (!tentry) { pr_err("SELinux: %s: unrecognized SID %d\n", __func__, tasksid); rc = -EINVAL; @@ -815,23 +817,25 @@ constraint = tclass_datum->validatetrans; while (constraint) { - if (!constraint_expr_eval(policydb, ocontext, ncontext, - tcontext, constraint->expr)) { + if (!constraint_expr_eval(policydb, &oentry->context, + &nentry->context, &tentry->context, + constraint->expr)) { if (user) rc = -EPERM; else rc = security_validtrans_handle_fail(state, - ocontext, - ncontext, - tcontext, - tclass); + policy, + oentry, + nentry, + tentry, + tclass); goto out; } constraint = constraint->next; } out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } @@ -863,32 +867,33 @@ int security_bounded_transition(struct selinux_state *state, u32 old_sid, u32 new_sid) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; - struct context *old_context, *new_context; + struct sidtab_entry *old_entry, *new_entry; struct type_datum *type; int index; int rc; - if (!state->initialized) + if (!selinux_initialized(state)) return 0; - read_lock(&state->ss->policy_rwlock); - - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; rc = -EINVAL; - old_context = sidtab_search(sidtab, old_sid); - if (!old_context) { + old_entry = sidtab_search_entry(sidtab, old_sid); + if (!old_entry) { pr_err("SELinux: %s: unrecognized SID %u\n", __func__, old_sid); goto out; } rc = -EINVAL; - new_context = sidtab_search(sidtab, new_sid); - if (!new_context) { + new_entry = sidtab_search_entry(sidtab, new_sid); + if (!new_entry) { pr_err("SELinux: %s: unrecognized SID %u\n", __func__, new_sid); goto out; @@ -896,13 +901,12 @@ rc = 0; /* type/domain unchanged */ - if (old_context->type == new_context->type) + if (old_entry->context.type == new_entry->context.type) goto out; - index = new_context->type; + index = new_entry->context.type; while (true) { - type = flex_array_get_ptr(policydb->type_val_to_struct_array, - index - 1); + type = policydb->type_val_to_struct[index - 1]; BUG_ON(!type); /* not bounded anymore */ @@ -912,7 +916,7 @@ /* @newsid is bounded by @oldsid */ rc = 0; - if (type->bounds == old_context->type) + if (type->bounds == old_entry->context.type) break; index = type->bounds; @@ -923,10 +927,10 @@ char *new_name = NULL; u32 length; - if (!context_struct_to_string(policydb, old_context, - &old_name, &length) && - !context_struct_to_string(policydb, new_context, - &new_name, &length)) { + if (!sidtab_entry_to_string(policydb, sidtab, old_entry, + &old_name, &length) && + !sidtab_entry_to_string(policydb, sidtab, new_entry, + &new_name, &length)) { audit_log(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR, "op=security_bounded_transition " @@ -938,17 +942,20 @@ kfree(old_name); } out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } -static void avd_init(struct selinux_state *state, struct av_decision *avd) +static void avd_init(struct selinux_policy *policy, struct av_decision *avd) { avd->allowed = 0; avd->auditallow = 0; avd->auditdeny = 0xffffffff; - avd->seqno = state->ss->latest_granting; + if (policy) + avd->seqno = policy->latest_granting; + else + avd->seqno = 0; avd->flags = 0; } @@ -1013,6 +1020,7 @@ u8 driver, struct extended_perms_decision *xpermd) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; u16 tclass; @@ -1029,12 +1037,13 @@ memset(xpermd->auditallow->p, 0, sizeof(xpermd->auditallow->p)); memset(xpermd->dontaudit->p, 0, sizeof(xpermd->dontaudit->p)); - read_lock(&state->ss->policy_rwlock); - if (!state->initialized) + rcu_read_lock(); + if (!selinux_initialized(state)) goto allow; - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; scontext = sidtab_search(sidtab, ssid); if (!scontext) { @@ -1050,7 +1059,7 @@ goto out; } - tclass = unmap_class(&state->ss->map, orig_tclass); + tclass = unmap_class(&policy->map, orig_tclass); if (unlikely(orig_tclass && !tclass)) { if (policydb->allow_unknown) goto allow; @@ -1065,12 +1074,8 @@ avkey.target_class = tclass; avkey.specified = AVTAB_XPERMS; - sattr = flex_array_get(policydb->type_attr_map_array, - scontext->type - 1); - BUG_ON(!sattr); - tattr = flex_array_get(policydb->type_attr_map_array, - tcontext->type - 1); - BUG_ON(!tattr); + sattr = &policydb->type_attr_map_array[scontext->type - 1]; + tattr = &policydb->type_attr_map_array[tcontext->type - 1]; ebitmap_for_each_positive_bit(sattr, snode, i) { ebitmap_for_each_positive_bit(tattr, tnode, j) { avkey.source_type = i + 1; @@ -1086,7 +1091,7 @@ } } out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return; allow: memset(xpermd->allowed->p, 0xff, sizeof(xpermd->allowed->p)); @@ -1111,19 +1116,21 @@ struct av_decision *avd, struct extended_perms *xperms) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; u16 tclass; struct context *scontext = NULL, *tcontext = NULL; - read_lock(&state->ss->policy_rwlock); - avd_init(state, avd); + rcu_read_lock(); + policy = rcu_dereference(state->policy); + avd_init(policy, avd); xperms->len = 0; - if (!state->initialized) + if (!selinux_initialized(state)) goto allow; - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; + policydb = &policy->policydb; + sidtab = policy->sidtab; scontext = sidtab_search(sidtab, ssid); if (!scontext) { @@ -1143,7 +1150,7 @@ goto out; } - tclass = unmap_class(&state->ss->map, orig_tclass); + tclass = unmap_class(&policy->map, orig_tclass); if (unlikely(orig_tclass && !tclass)) { if (policydb->allow_unknown) goto allow; @@ -1151,10 +1158,10 @@ } context_struct_compute_av(policydb, scontext, tcontext, tclass, avd, xperms); - map_decision(&state->ss->map, orig_tclass, avd, + map_decision(&policy->map, orig_tclass, avd, policydb->allow_unknown); out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return; allow: avd->allowed = 0xffffffff; @@ -1167,17 +1174,19 @@ u16 tclass, struct av_decision *avd) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; struct context *scontext = NULL, *tcontext = NULL; - read_lock(&state->ss->policy_rwlock); - avd_init(state, avd); - if (!state->initialized) + rcu_read_lock(); + policy = rcu_dereference(state->policy); + avd_init(policy, avd); + if (!selinux_initialized(state)) goto allow; - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; + policydb = &policy->policydb; + sidtab = policy->sidtab; scontext = sidtab_search(sidtab, ssid); if (!scontext) { @@ -1206,7 +1215,7 @@ context_struct_compute_av(policydb, scontext, tcontext, tclass, avd, NULL); out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return; allow: avd->allowed = 0xffffffff; @@ -1270,21 +1279,40 @@ return 0; } +static int sidtab_entry_to_string(struct policydb *p, + struct sidtab *sidtab, + struct sidtab_entry *entry, + char **scontext, u32 *scontext_len) +{ + int rc = sidtab_sid2str_get(sidtab, entry, scontext, scontext_len); + + if (rc != -ENOENT) + return rc; + + rc = context_struct_to_string(p, &entry->context, scontext, + scontext_len); + if (!rc && scontext) + sidtab_sid2str_put(sidtab, entry, *scontext, *scontext_len); + return rc; +} + #include "initial_sid_to_string.h" int security_sidtab_hash_stats(struct selinux_state *state, char *page) { + struct selinux_policy *policy; int rc; - if (!state->initialized) { + if (!selinux_initialized(state)) { pr_err("SELinux: %s: called before initial load_policy\n", __func__); return -EINVAL; } - read_lock(&state->ss->policy_rwlock); - rc = sidtab_hash_stats(state->ss->sidtab, page); - read_unlock(&state->ss->policy_rwlock); + rcu_read_lock(); + policy = rcu_dereference(state->policy); + rc = sidtab_hash_stats(policy->sidtab, page); + rcu_read_unlock(); return rc; } @@ -1298,56 +1326,62 @@ static int security_sid_to_context_core(struct selinux_state *state, u32 sid, char **scontext, - u32 *scontext_len, int force) + u32 *scontext_len, int force, + int only_invalid) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; - struct context *context; + struct sidtab_entry *entry; int rc = 0; if (scontext) *scontext = NULL; *scontext_len = 0; - if (!state->initialized) { + if (!selinux_initialized(state)) { if (sid <= SECINITSID_NUM) { char *scontextp; + const char *s = initial_sid_to_string[sid]; - *scontext_len = strlen(initial_sid_to_string[sid]) + 1; + if (!s) + return -EINVAL; + *scontext_len = strlen(s) + 1; if (!scontext) - goto out; - scontextp = kmemdup(initial_sid_to_string[sid], - *scontext_len, GFP_ATOMIC); - if (!scontextp) { - rc = -ENOMEM; - goto out; - } + return 0; + scontextp = kmemdup(s, *scontext_len, GFP_ATOMIC); + if (!scontextp) + return -ENOMEM; *scontext = scontextp; - goto out; + return 0; } pr_err("SELinux: %s: called before initial " "load_policy on unknown SID %d\n", __func__, sid); - rc = -EINVAL; - goto out; + return -EINVAL; } - read_lock(&state->ss->policy_rwlock); - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; + if (force) - context = sidtab_search_force(sidtab, sid); + entry = sidtab_search_entry_force(sidtab, sid); else - context = sidtab_search(sidtab, sid); - if (!context) { + entry = sidtab_search_entry(sidtab, sid); + if (!entry) { pr_err("SELinux: %s: unrecognized SID %d\n", __func__, sid); rc = -EINVAL; goto out_unlock; } - rc = context_struct_to_string(policydb, context, scontext, - scontext_len); + if (only_invalid && !entry->context.len) + goto out_unlock; + + rc = sidtab_entry_to_string(policydb, sidtab, entry, scontext, + scontext_len); + out_unlock: - read_unlock(&state->ss->policy_rwlock); -out: + rcu_read_unlock(); return rc; } @@ -1366,14 +1400,34 @@ u32 sid, char **scontext, u32 *scontext_len) { return security_sid_to_context_core(state, sid, scontext, - scontext_len, 0); + scontext_len, 0, 0); } int security_sid_to_context_force(struct selinux_state *state, u32 sid, char **scontext, u32 *scontext_len) { return security_sid_to_context_core(state, sid, scontext, - scontext_len, 1); + scontext_len, 1, 0); +} + +/** + * security_sid_to_context_inval - Obtain a context for a given SID if it + * is invalid. + * @sid: security identifier, SID + * @scontext: security context + * @scontext_len: length in bytes + * + * Write the string representation of the context associated with @sid + * into a dynamically allocated string of the correct size, but only if the + * context is invalid in the current policy. Set @scontext to point to + * this string (or NULL if the context is valid) and set @scontext_len to + * the length of the string (or 0 if the context is valid). + */ +int security_sid_to_context_inval(struct selinux_state *state, u32 sid, + char **scontext, u32 *scontext_len) +{ + return security_sid_to_context_core(state, sid, scontext, + scontext_len, 1, 1); } /* @@ -1408,7 +1462,7 @@ *p++ = 0; - usrdatum = hashtab_search(pol->p_users.table, scontextp); + usrdatum = symtab_search(&pol->p_users, scontextp); if (!usrdatum) goto out; @@ -1424,7 +1478,7 @@ *p++ = 0; - role = hashtab_search(pol->p_roles.table, scontextp); + role = symtab_search(&pol->p_roles, scontextp); if (!role) goto out; ctx->role = role->value; @@ -1436,7 +1490,7 @@ oldc = *p; *p++ = 0; - typdatum = hashtab_search(pol->p_types.table, scontextp); + typdatum = symtab_search(&pol->p_types, scontextp); if (!typdatum || typdatum->attribute) goto out; @@ -1457,47 +1511,12 @@ return rc; } -int context_add_hash(struct policydb *policydb, - struct context *context) -{ - int rc; - char *str; - int len; - - if (context->str) { - context->hash = context_compute_hash(context->str); - } else { - rc = context_struct_to_string(policydb, context, - &str, &len); - if (rc) - return rc; - context->hash = context_compute_hash(str); - kfree(str); - } - return 0; -} - -static int context_struct_to_sid(struct selinux_state *state, - struct context *context, u32 *sid) -{ - int rc; - struct sidtab *sidtab = state->ss->sidtab; - struct policydb *policydb = &state->ss->policydb; - - if (!context->hash) { - rc = context_add_hash(policydb, context); - if (rc) - return rc; - } - - return sidtab_context_to_sid(sidtab, context, sid); -} - static int security_context_to_sid_core(struct selinux_state *state, const char *scontext, u32 scontext_len, u32 *sid, u32 def_sid, gfp_t gfp_flags, int force) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; char *scontext2, *str = NULL; @@ -1513,11 +1532,13 @@ if (!scontext2) return -ENOMEM; - if (!state->initialized) { + if (!selinux_initialized(state)) { int i; for (i = 1; i < SECINITSID_NUM; i++) { - if (!strcmp(initial_sid_to_string[i], scontext2)) { + const char *s = initial_sid_to_string[i]; + + if (s && !strcmp(s, scontext2)) { *sid = i; goto out; } @@ -1534,9 +1555,11 @@ if (!str) goto out; } - read_lock(&state->ss->policy_rwlock); - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; +retry: + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; rc = string_to_context_struct(policydb, sidtab, scontext2, &context, def_sid); if (rc == -EINVAL && force) { @@ -1545,10 +1568,19 @@ str = NULL; } else if (rc) goto out_unlock; - rc = context_struct_to_sid(state, &context, sid); + rc = sidtab_context_to_sid(sidtab, &context, sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + if (context.str) { + str = context.str; + context.str = NULL; + } + context_destroy(&context); + goto retry; + } context_destroy(&context); out_unlock: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); out: kfree(scontext2); kfree(str); @@ -1618,27 +1650,32 @@ static int compute_sid_handle_invalid_context( struct selinux_state *state, - struct context *scontext, - struct context *tcontext, + struct selinux_policy *policy, + struct sidtab_entry *sentry, + struct sidtab_entry *tentry, u16 tclass, struct context *newcontext) { - struct policydb *policydb = &state->ss->policydb; + struct policydb *policydb = &policy->policydb; + struct sidtab *sidtab = policy->sidtab; char *s = NULL, *t = NULL, *n = NULL; u32 slen, tlen, nlen; + struct audit_buffer *ab; - if (context_struct_to_string(policydb, scontext, &s, &slen)) + if (sidtab_entry_to_string(policydb, sidtab, sentry, &s, &slen)) goto out; - if (context_struct_to_string(policydb, tcontext, &t, &tlen)) + if (sidtab_entry_to_string(policydb, sidtab, tentry, &t, &tlen)) goto out; if (context_struct_to_string(policydb, newcontext, &n, &nlen)) goto out; - audit_log(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR, - "op=security_compute_sid invalid_context=%s" - " scontext=%s" - " tcontext=%s" - " tclass=%s", - n, s, t, sym_name(policydb, SYM_CLASSES, tclass-1)); + ab = audit_log_start(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR); + audit_log_format(ab, + "op=security_compute_sid invalid_context="); + /* no need to record the NUL with untrusted strings */ + audit_log_n_untrustedstring(ab, n, nlen - 1); + audit_log_format(ab, " scontext=%s tcontext=%s tclass=%s", + s, t, sym_name(policydb, SYM_CLASSES, tclass-1)); + audit_log_end(ab); out: kfree(s); kfree(t); @@ -1653,8 +1690,8 @@ u32 stype, u32 ttype, u16 tclass, const char *objname) { - struct filename_trans ft; - struct filename_trans_datum *otype; + struct filename_trans_key ft; + struct filename_trans_datum *datum; /* * Most filename trans rules are going to live in specific directories @@ -1664,14 +1701,18 @@ if (!ebitmap_get_bit(&policydb->filename_trans_ttypes, ttype)) return; - ft.stype = stype; ft.ttype = ttype; ft.tclass = tclass; ft.name = objname; - otype = hashtab_search(policydb->filename_trans, &ft); - if (otype) - newcontext->type = otype->otype; + datum = policydb_filenametr_search(policydb, &ft); + while (datum) { + if (ebitmap_get_bit(&datum->stypes, stype - 1)) { + newcontext->type = datum->otype; + return; + } + datum = datum->next; + } } static int security_compute_sid(struct selinux_state *state, @@ -1683,11 +1724,12 @@ u32 *out_sid, bool kern) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; - struct class_datum *cladatum = NULL; - struct context *scontext = NULL, *tcontext = NULL, newcontext; - struct role_trans *roletr = NULL; + struct class_datum *cladatum; + struct context *scontext, *tcontext, newcontext; + struct sidtab_entry *sentry, *tentry; struct avtab_key avkey; struct avtab_datum *avdatum; struct avtab_node *node; @@ -1695,7 +1737,7 @@ int rc = 0; bool sock; - if (!state->initialized) { + if (!selinux_initialized(state)) { switch (orig_tclass) { case SECCLASS_PROCESS: /* kernel value */ *out_sid = ssid; @@ -1707,36 +1749,43 @@ goto out; } +retry: + cladatum = NULL; context_init(&newcontext); - read_lock(&state->ss->policy_rwlock); + rcu_read_lock(); + + policy = rcu_dereference(state->policy); if (kern) { - tclass = unmap_class(&state->ss->map, orig_tclass); + tclass = unmap_class(&policy->map, orig_tclass); sock = security_is_socket_class(orig_tclass); } else { tclass = orig_tclass; - sock = security_is_socket_class(map_class(&state->ss->map, + sock = security_is_socket_class(map_class(&policy->map, tclass)); } - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; + policydb = &policy->policydb; + sidtab = policy->sidtab; - scontext = sidtab_search(sidtab, ssid); - if (!scontext) { + sentry = sidtab_search_entry(sidtab, ssid); + if (!sentry) { pr_err("SELinux: %s: unrecognized SID %d\n", __func__, ssid); rc = -EINVAL; goto out_unlock; } - tcontext = sidtab_search(sidtab, tsid); - if (!tcontext) { + tentry = sidtab_search_entry(sidtab, tsid); + if (!tentry) { pr_err("SELinux: %s: unrecognized SID %d\n", __func__, tsid); rc = -EINVAL; goto out_unlock; } + + scontext = &sentry->context; + tcontext = &tentry->context; if (tclass && tclass <= policydb->p_classes.nprim) cladatum = policydb->class_val_to_struct[tclass - 1]; @@ -1765,7 +1814,7 @@ } else if (cladatum && cladatum->default_role == DEFAULT_TARGET) { newcontext.role = tcontext->role; } else { - if ((tclass == policydb->process_class) || (sock == true)) + if ((tclass == policydb->process_class) || sock) newcontext.role = scontext->role; else newcontext.role = OBJECT_R_VAL; @@ -1777,7 +1826,7 @@ } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) { newcontext.type = tcontext->type; } else { - if ((tclass == policydb->process_class) || (sock == true)) { + if ((tclass == policydb->process_class) || sock) { /* Use the type of process. */ newcontext.type = scontext->type; } else { @@ -1817,16 +1866,16 @@ /* Check for class-specific changes. */ if (specified & AVTAB_TRANSITION) { /* Look for a role transition rule. */ - for (roletr = policydb->role_tr; roletr; - roletr = roletr->next) { - if ((roletr->role == scontext->role) && - (roletr->type == tcontext->type) && - (roletr->tclass == tclass)) { - /* Use the role transition rule. */ - newcontext.role = roletr->new_role; - break; - } - } + struct role_trans_datum *rtd; + struct role_trans_key rtk = { + .role = scontext->role, + .type = tcontext->type, + .tclass = tclass, + }; + + rtd = policydb_roletr_search(policydb, &rtk); + if (rtd) + newcontext.role = rtd->new_role; } /* Set the MLS attributes. @@ -1838,17 +1887,21 @@ /* Check the validity of the context. */ if (!policydb_context_isvalid(policydb, &newcontext)) { - rc = compute_sid_handle_invalid_context(state, scontext, - tcontext, - tclass, + rc = compute_sid_handle_invalid_context(state, policy, sentry, + tentry, tclass, &newcontext); if (rc) goto out_unlock; } /* Obtain the sid for the context. */ - rc = context_struct_to_sid(state, &newcontext, out_sid); + rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + context_destroy(&newcontext); + goto retry; + } out_unlock: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); context_destroy(&newcontext); out: return rc; @@ -1935,9 +1988,9 @@ static inline int convert_context_handle_invalid_context( struct selinux_state *state, + struct policydb *policydb, struct context *context) { - struct policydb *policydb = &state->ss->policydb; char *s; u32 len; @@ -1952,12 +2005,6 @@ return 0; } -struct convert_context_args { - struct selinux_state *state; - struct policydb *oldp; - struct policydb *newp; -}; - /* * Convert the values in the security context * structure `oldc' from the values specified @@ -1966,7 +2013,8 @@ * in `newc'. Verify that the context is valid * under the new policy. */ -static int convert_context(struct context *oldc, struct context *newc, void *p) +static int convert_context(struct context *oldc, struct context *newc, void *p, + gfp_t gfp_flags) { struct convert_context_args *args; struct ocontext *oc; @@ -1980,7 +2028,7 @@ args = p; if (oldc->str) { - s = kstrdup(oldc->str, GFP_KERNEL); + s = kstrdup(oldc->str, gfp_flags); if (!s) return -ENOMEM; @@ -1998,7 +2046,6 @@ context_init(newc); newc->str = s; newc->len = oldc->len; - newc->hash = oldc->hash; return 0; } kfree(s); @@ -2017,26 +2064,26 @@ /* Convert the user. */ rc = -EINVAL; - usrdatum = hashtab_search(args->newp->p_users.table, - sym_name(args->oldp, - SYM_USERS, oldc->user - 1)); + usrdatum = symtab_search(&args->newp->p_users, + sym_name(args->oldp, + SYM_USERS, oldc->user - 1)); if (!usrdatum) goto bad; newc->user = usrdatum->value; /* Convert the role. */ rc = -EINVAL; - role = hashtab_search(args->newp->p_roles.table, - sym_name(args->oldp, SYM_ROLES, oldc->role - 1)); + role = symtab_search(&args->newp->p_roles, + sym_name(args->oldp, SYM_ROLES, oldc->role - 1)); if (!role) goto bad; newc->role = role->value; /* Convert the type. */ rc = -EINVAL; - typdatum = hashtab_search(args->newp->p_types.table, - sym_name(args->oldp, - SYM_TYPES, oldc->type - 1)); + typdatum = symtab_search(&args->newp->p_types, + sym_name(args->oldp, + SYM_TYPES, oldc->type - 1)); if (!typdatum) goto bad; newc->type = typdatum->value; @@ -2070,14 +2117,12 @@ /* Check the validity of the new context. */ if (!policydb_context_isvalid(args->newp, newc)) { - rc = convert_context_handle_invalid_context(args->state, oldc); + rc = convert_context_handle_invalid_context(args->state, + args->oldp, + oldc); if (rc) goto bad; } - - rc = context_add_hash(args->newp, newc); - if (rc) - goto bad; return 0; bad: @@ -2088,20 +2133,23 @@ context_destroy(newc); newc->str = s; newc->len = len; - newc->hash = context_compute_hash(s); pr_info("SELinux: Context %s became invalid (unmapped).\n", newc->str); return 0; } -static void security_load_policycaps(struct selinux_state *state) +static void security_load_policycaps(struct selinux_state *state, + struct selinux_policy *policy) { - struct policydb *p = &state->ss->policydb; + struct policydb *p; unsigned int i; struct ebitmap_node *node; + p = &policy->policydb; + for (i = 0; i < ARRAY_SIZE(state->policycap); i++) - state->policycap[i] = ebitmap_get_bit(&p->policycaps, i); + WRITE_ONCE(state->policycap[i], + ebitmap_get_bit(&p->policycaps, i)); for (i = 0; i < ARRAY_SIZE(selinux_policycap_names); i++) pr_info("SELinux: policy capability %s=%d\n", @@ -2119,8 +2167,107 @@ selinux_nlmsg_init(); } -static int security_preserve_bools(struct selinux_state *state, - struct policydb *newpolicydb); +static int security_preserve_bools(struct selinux_policy *oldpolicy, + struct selinux_policy *newpolicy); + +static void selinux_policy_free(struct selinux_policy *policy) +{ + if (!policy) + return; + + sidtab_destroy(policy->sidtab); + kfree(policy->map.mapping); + policydb_destroy(&policy->policydb); + kfree(policy->sidtab); + kfree(policy); +} + +static void selinux_policy_cond_free(struct selinux_policy *policy) +{ + cond_policydb_destroy_dup(&policy->policydb); + kfree(policy); +} + +void selinux_policy_cancel(struct selinux_state *state, + struct selinux_load_state *load_state) +{ + struct selinux_policy *oldpolicy; + + oldpolicy = rcu_dereference_protected(state->policy, + lockdep_is_held(&state->policy_mutex)); + + sidtab_cancel_convert(oldpolicy->sidtab); + selinux_policy_free(load_state->policy); + kfree(load_state->convert_data); +} + +static void selinux_notify_policy_change(struct selinux_state *state, + u32 seqno) +{ + /* Flush external caches and notify userspace of policy load */ + avc_ss_reset(state->avc, seqno); + selnl_notify_policyload(seqno); + selinux_status_update_policyload(state, seqno); + selinux_netlbl_cache_invalidate(); + selinux_xfrm_notify_policyload(); +} + +void selinux_policy_commit(struct selinux_state *state, + struct selinux_load_state *load_state) +{ + struct selinux_policy *oldpolicy, *newpolicy = load_state->policy; + unsigned long flags; + u32 seqno; + + oldpolicy = rcu_dereference_protected(state->policy, + lockdep_is_held(&state->policy_mutex)); + + /* If switching between different policy types, log MLS status */ + if (oldpolicy) { + if (oldpolicy->policydb.mls_enabled && !newpolicy->policydb.mls_enabled) + pr_info("SELinux: Disabling MLS support...\n"); + else if (!oldpolicy->policydb.mls_enabled && newpolicy->policydb.mls_enabled) + pr_info("SELinux: Enabling MLS support...\n"); + } + + /* Set latest granting seqno for new policy. */ + if (oldpolicy) + newpolicy->latest_granting = oldpolicy->latest_granting + 1; + else + newpolicy->latest_granting = 1; + seqno = newpolicy->latest_granting; + + /* Install the new policy. */ + if (oldpolicy) { + sidtab_freeze_begin(oldpolicy->sidtab, &flags); + rcu_assign_pointer(state->policy, newpolicy); + sidtab_freeze_end(oldpolicy->sidtab, &flags); + } else { + rcu_assign_pointer(state->policy, newpolicy); + } + + /* Load the policycaps from the new policy */ + security_load_policycaps(state, newpolicy); + + if (!selinux_initialized(state)) { + /* + * After first policy load, the security server is + * marked as initialized and ready to handle requests and + * any objects created prior to policy load are then labeled. + */ + selinux_mark_initialized(state); + selinux_complete_init(); + trace_android_vh_selinux_is_initialized(state); + } + + /* Free the old policy */ + synchronize_rcu(); + selinux_policy_free(oldpolicy); + kfree(load_state->convert_data); + + /* Notify others of the policy change */ + selinux_notify_policy_change(state, seqno); +} /** * security_load_policy - Load a security policy configuration. @@ -2132,173 +2279,138 @@ * This function will flush the access vector cache after * loading the new policy. */ -int security_load_policy(struct selinux_state *state, void *data, size_t len) +int security_load_policy(struct selinux_state *state, void *data, size_t len, + struct selinux_load_state *load_state) { - struct policydb *policydb; - struct sidtab *oldsidtab, *newsidtab; - struct policydb *oldpolicydb, *newpolicydb; - struct selinux_mapping *oldmapping; - struct selinux_map newmap; - struct sidtab_convert_params convert_params; - struct convert_context_args args; - u32 seqno; + struct selinux_policy *newpolicy, *oldpolicy; + struct selinux_policy_convert_data *convert_data; int rc = 0; struct policy_file file = { data, len }, *fp = &file; - oldpolicydb = kcalloc(2, sizeof(*oldpolicydb), GFP_KERNEL); - if (!oldpolicydb) { + newpolicy = kzalloc(sizeof(*newpolicy), GFP_KERNEL); + if (!newpolicy) + return -ENOMEM; + + newpolicy->sidtab = kzalloc(sizeof(*newpolicy->sidtab), GFP_KERNEL); + if (!newpolicy->sidtab) { rc = -ENOMEM; - goto out; - } - newpolicydb = oldpolicydb + 1; - - policydb = &state->ss->policydb; - - newsidtab = kmalloc(sizeof(*newsidtab), GFP_KERNEL); - if (!newsidtab) { - rc = -ENOMEM; - goto out; + goto err_policy; } - if (!state->initialized) { - rc = policydb_read(policydb, fp); - if (rc) { - kfree(newsidtab); - goto out; - } + rc = policydb_read(&newpolicy->policydb, fp); + if (rc) + goto err_sidtab; - policydb->len = len; - rc = selinux_set_mapping(policydb, secclass_map, - &state->ss->map); - if (rc) { - kfree(newsidtab); - policydb_destroy(policydb); - goto out; - } + newpolicy->policydb.len = len; + rc = selinux_set_mapping(&newpolicy->policydb, secclass_map, + &newpolicy->map); + if (rc) + goto err_policydb; - rc = policydb_load_isids(policydb, newsidtab); - if (rc) { - kfree(newsidtab); - policydb_destroy(policydb); - goto out; - } - - state->ss->sidtab = newsidtab; - security_load_policycaps(state); - state->initialized = 1; - seqno = ++state->ss->latest_granting; - selinux_complete_init(); - avc_ss_reset(state->avc, seqno); - selnl_notify_policyload(seqno); - selinux_status_update_policyload(state, seqno); - selinux_netlbl_cache_invalidate(); - selinux_xfrm_notify_policyload(); - goto out; - } - - rc = policydb_read(newpolicydb, fp); - if (rc) { - kfree(newsidtab); - goto out; - } - - newpolicydb->len = len; - /* If switching between different policy types, log MLS status */ - if (policydb->mls_enabled && !newpolicydb->mls_enabled) - pr_info("SELinux: Disabling MLS support...\n"); - else if (!policydb->mls_enabled && newpolicydb->mls_enabled) - pr_info("SELinux: Enabling MLS support...\n"); - - rc = policydb_load_isids(newpolicydb, newsidtab); + rc = policydb_load_isids(&newpolicy->policydb, newpolicy->sidtab); if (rc) { pr_err("SELinux: unable to load the initial SIDs\n"); - policydb_destroy(newpolicydb); - kfree(newsidtab); - goto out; + goto err_mapping; } - rc = selinux_set_mapping(newpolicydb, secclass_map, &newmap); - if (rc) - goto err; + if (!selinux_initialized(state)) { + /* First policy load, so no need to preserve state from old policy */ + load_state->policy = newpolicy; + load_state->convert_data = NULL; + return 0; + } - rc = security_preserve_bools(state, newpolicydb); + oldpolicy = rcu_dereference_protected(state->policy, + lockdep_is_held(&state->policy_mutex)); + + /* Preserve active boolean values from the old policy */ + rc = security_preserve_bools(oldpolicy, newpolicy); if (rc) { pr_err("SELinux: unable to preserve booleans\n"); - goto err; + goto err_free_isids; } - oldsidtab = state->ss->sidtab; + convert_data = kmalloc(sizeof(*convert_data), GFP_KERNEL); + if (!convert_data) { + rc = -ENOMEM; + goto err_free_isids; + } /* * Convert the internal representations of contexts * in the new SID table. */ - args.state = state; - args.oldp = policydb; - args.newp = newpolicydb; + convert_data->args.state = state; + convert_data->args.oldp = &oldpolicy->policydb; + convert_data->args.newp = &newpolicy->policydb; - convert_params.func = convert_context; - convert_params.args = &args; - convert_params.target = newsidtab; + convert_data->sidtab_params.func = convert_context; + convert_data->sidtab_params.args = &convert_data->args; + convert_data->sidtab_params.target = newpolicy->sidtab; - rc = sidtab_convert(oldsidtab, &convert_params); + rc = sidtab_convert(oldpolicy->sidtab, &convert_data->sidtab_params); if (rc) { pr_err("SELinux: unable to convert the internal" " representation of contexts in the new SID" " table\n"); - goto err; + goto err_free_convert_data; } - /* Save the old policydb and SID table to free later. */ - memcpy(oldpolicydb, policydb, sizeof(*policydb)); + load_state->policy = newpolicy; + load_state->convert_data = convert_data; + return 0; - /* Install the new policydb and SID table. */ - write_lock_irq(&state->ss->policy_rwlock); - memcpy(policydb, newpolicydb, sizeof(*policydb)); - state->ss->sidtab = newsidtab; - security_load_policycaps(state); - oldmapping = state->ss->map.mapping; - state->ss->map.mapping = newmap.mapping; - state->ss->map.size = newmap.size; - seqno = ++state->ss->latest_granting; - write_unlock_irq(&state->ss->policy_rwlock); +err_free_convert_data: + kfree(convert_data); +err_free_isids: + sidtab_destroy(newpolicy->sidtab); +err_mapping: + kfree(newpolicy->map.mapping); +err_policydb: + policydb_destroy(&newpolicy->policydb); +err_sidtab: + kfree(newpolicy->sidtab); +err_policy: + kfree(newpolicy); - /* Free the old policydb and SID table. */ - policydb_destroy(oldpolicydb); - sidtab_destroy(oldsidtab); - kfree(oldsidtab); - kfree(oldmapping); - - avc_ss_reset(state->avc, seqno); - selnl_notify_policyload(seqno); - selinux_status_update_policyload(state, seqno); - selinux_netlbl_cache_invalidate(); - selinux_xfrm_notify_policyload(); - - rc = 0; - goto out; - -err: - kfree(newmap.mapping); - sidtab_destroy(newsidtab); - kfree(newsidtab); - policydb_destroy(newpolicydb); - -out: - kfree(oldpolicydb); return rc; } -size_t security_policydb_len(struct selinux_state *state) +/** + * ocontext_to_sid - Helper to safely get sid for an ocontext + * @sidtab: SID table + * @c: ocontext structure + * @index: index of the context entry (0 or 1) + * @out_sid: pointer to the resulting SID value + * + * For all ocontexts except OCON_ISID the SID fields are populated + * on-demand when needed. Since updating the SID value is an SMP-sensitive + * operation, this helper must be used to do that safely. + * + * WARNING: This function may return -ESTALE, indicating that the caller + * must retry the operation after re-acquiring the policy pointer! + */ +static int ocontext_to_sid(struct sidtab *sidtab, struct ocontext *c, + size_t index, u32 *out_sid) { - struct policydb *p = &state->ss->policydb; - size_t len; + int rc; + u32 sid; - read_lock(&state->ss->policy_rwlock); - len = p->len; - read_unlock(&state->ss->policy_rwlock); + /* Ensure the associated sidtab entry is visible to this thread. */ + sid = smp_load_acquire(&c->sid[index]); + if (!sid) { + rc = sidtab_context_to_sid(sidtab, &c->context[index], &sid); + if (rc) + return rc; - return len; + /* + * Ensure the new sidtab entry is visible to other threads + * when they see the SID. + */ + smp_store_release(&c->sid[index], sid); + } + *out_sid = sid; + return 0; } /** @@ -2310,15 +2422,23 @@ int security_port_sid(struct selinux_state *state, u8 protocol, u16 port, u32 *out_sid) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; struct ocontext *c; - int rc = 0; + int rc; - read_lock(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) { + *out_sid = SECINITSID_PORT; + return 0; + } - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; +retry: + rc = 0; + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; c = policydb->ocontexts[OCON_PORT]; while (c) { @@ -2330,19 +2450,19 @@ } if (c) { - if (!c->sid[0]) { - rc = context_struct_to_sid(state, &c->context[0], - &c->sid[0]); - if (rc) - goto out; + rc = ocontext_to_sid(sidtab, c, 0, out_sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + goto retry; } - *out_sid = c->sid[0]; + if (rc) + goto out; } else { *out_sid = SECINITSID_PORT; } out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } @@ -2355,13 +2475,23 @@ int security_ib_pkey_sid(struct selinux_state *state, u64 subnet_prefix, u16 pkey_num, u32 *out_sid) { + struct selinux_policy *policy; struct policydb *policydb; + struct sidtab *sidtab; struct ocontext *c; - int rc = 0; + int rc; - read_lock(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) { + *out_sid = SECINITSID_UNLABELED; + return 0; + } - policydb = &state->ss->policydb; +retry: + rc = 0; + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; c = policydb->ocontexts[OCON_IBPKEY]; while (c) { @@ -2374,19 +2504,18 @@ } if (c) { - if (!c->sid[0]) { - rc = context_struct_to_sid(state, - &c->context[0], - &c->sid[0]); - if (rc) - goto out; + rc = ocontext_to_sid(sidtab, c, 0, out_sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + goto retry; } - *out_sid = c->sid[0]; + if (rc) + goto out; } else *out_sid = SECINITSID_UNLABELED; out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } @@ -2399,15 +2528,23 @@ int security_ib_endport_sid(struct selinux_state *state, const char *dev_name, u8 port_num, u32 *out_sid) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; struct ocontext *c; - int rc = 0; + int rc; - read_lock(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) { + *out_sid = SECINITSID_UNLABELED; + return 0; + } - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; +retry: + rc = 0; + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; c = policydb->ocontexts[OCON_IBENDPORT]; while (c) { @@ -2421,18 +2558,18 @@ } if (c) { - if (!c->sid[0]) { - rc = context_struct_to_sid(state, &c->context[0], - &c->sid[0]); - if (rc) - goto out; + rc = ocontext_to_sid(sidtab, c, 0, out_sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + goto retry; } - *out_sid = c->sid[0]; + if (rc) + goto out; } else *out_sid = SECINITSID_UNLABELED; out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } @@ -2444,15 +2581,23 @@ int security_netif_sid(struct selinux_state *state, char *name, u32 *if_sid) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; - int rc = 0; + int rc; struct ocontext *c; - read_lock(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) { + *if_sid = SECINITSID_NETIF; + return 0; + } - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; +retry: + rc = 0; + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; c = policydb->ocontexts[OCON_NETIF]; while (c) { @@ -2462,22 +2607,18 @@ } if (c) { - if (!c->sid[0] || !c->sid[1]) { - rc = context_struct_to_sid(state, &c->context[0], - &c->sid[0]); - if (rc) - goto out; - rc = context_struct_to_sid(state, &c->context[1], - &c->sid[1]); - if (rc) - goto out; + rc = ocontext_to_sid(sidtab, c, 0, if_sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + goto retry; } - *if_sid = c->sid[0]; + if (rc) + goto out; } else *if_sid = SECINITSID_NETIF; out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } @@ -2507,13 +2648,22 @@ u32 addrlen, u32 *out_sid) { + struct selinux_policy *policy; struct policydb *policydb; + struct sidtab *sidtab; int rc; struct ocontext *c; - read_lock(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) { + *out_sid = SECINITSID_NODE; + return 0; + } - policydb = &state->ss->policydb; +retry: + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; switch (domain) { case AF_INET: { @@ -2554,21 +2704,20 @@ } if (c) { - if (!c->sid[0]) { - rc = context_struct_to_sid(state, - &c->context[0], - &c->sid[0]); - if (rc) - goto out; + rc = ocontext_to_sid(sidtab, c, 0, out_sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + goto retry; } - *out_sid = c->sid[0]; + if (rc) + goto out; } else { *out_sid = SECINITSID_NODE; } rc = 0; out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } @@ -2594,26 +2743,33 @@ u32 **sids, u32 *nel) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; struct context *fromcon, usercon; u32 *mysids = NULL, *mysids2, sid; - u32 mynel = 0, maxnel = SIDS_NEL; + u32 i, j, mynel, maxnel = SIDS_NEL; struct user_datum *user; struct role_datum *role; struct ebitmap_node *rnode, *tnode; - int rc = 0, i, j; + int rc; *sids = NULL; *nel = 0; - if (!state->initialized) - goto out; + if (!selinux_initialized(state)) + return 0; - read_lock(&state->ss->policy_rwlock); + mysids = kcalloc(maxnel, sizeof(*mysids), GFP_KERNEL); + if (!mysids) + return -ENOMEM; - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; +retry: + mynel = 0; + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; context_init(&usercon); @@ -2623,33 +2779,27 @@ goto out_unlock; rc = -EINVAL; - user = hashtab_search(policydb->p_users.table, username); + user = symtab_search(&policydb->p_users, username); if (!user) goto out_unlock; usercon.user = user->value; - - rc = -ENOMEM; - mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC); - if (!mysids) - goto out_unlock; ebitmap_for_each_positive_bit(&user->roles, rnode, i) { role = policydb->role_val_to_struct[i]; usercon.role = i + 1; ebitmap_for_each_positive_bit(&role->types, tnode, j) { usercon.type = j + 1; - /* - * The same context struct is reused here so the hash - * must be reset. - */ - usercon.hash = 0; if (mls_setup_user_range(policydb, fromcon, user, &usercon)) continue; - rc = context_struct_to_sid(state, &usercon, &sid); + rc = sidtab_context_to_sid(sidtab, &usercon, &sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + goto retry; + } if (rc) goto out_unlock; if (mynel < maxnel) { @@ -2669,17 +2819,17 @@ } rc = 0; out_unlock: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); if (rc || !mynel) { kfree(mysids); - goto out; + return rc; } rc = -ENOMEM; mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL); if (!mysids2) { kfree(mysids); - goto out; + return rc; } for (i = 0, j = 0; i < mynel; i++) { struct av_decision dummy_avd; @@ -2692,12 +2842,10 @@ mysids2[j++] = mysids[i]; cond_resched(); } - rc = 0; kfree(mysids); *sids = mysids2; *nel = j; -out: - return rc; + return 0; } /** @@ -2711,25 +2859,27 @@ * cannot support xattr or use a fixed labeling behavior like * transition SIDs or task SIDs. * - * The caller must acquire the policy_rwlock before calling this function. + * WARNING: This function may return -ESTALE, indicating that the caller + * must retry the operation after re-acquiring the policy pointer! */ -static inline int __security_genfs_sid(struct selinux_state *state, +static inline int __security_genfs_sid(struct selinux_policy *policy, const char *fstype, char *path, u16 orig_sclass, u32 *sid) { - struct policydb *policydb = &state->ss->policydb; + struct policydb *policydb = &policy->policydb; + struct sidtab *sidtab = policy->sidtab; int len; u16 sclass; struct genfs *genfs; struct ocontext *c; - int rc, cmp = 0; + int cmp = 0; while (path[0] == '/' && path[1] == '/') path++; - sclass = unmap_class(&state->ss->map, orig_sclass); + sclass = unmap_class(&policy->map, orig_sclass); *sid = SECINITSID_UNLABELED; for (genfs = policydb->genfs; genfs; genfs = genfs->next) { @@ -2738,9 +2888,8 @@ break; } - rc = -ENOENT; if (!genfs || cmp) - goto out; + return -ENOENT; for (c = genfs->head; c; c = c->next) { len = strlen(c->u.name); @@ -2749,20 +2898,10 @@ break; } - rc = -ENOENT; if (!c) - goto out; + return -ENOENT; - if (!c->sid[0]) { - rc = context_struct_to_sid(state, &c->context[0], &c->sid[0]); - if (rc) - goto out; - } - - *sid = c->sid[0]; - rc = 0; -out: - return rc; + return ocontext_to_sid(sidtab, c, 0, sid); } /** @@ -2781,12 +2920,32 @@ u16 orig_sclass, u32 *sid) { + struct selinux_policy *policy; int retval; - read_lock(&state->ss->policy_rwlock); - retval = __security_genfs_sid(state, fstype, path, orig_sclass, sid); - read_unlock(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) { + *sid = SECINITSID_UNLABELED; + return 0; + } + + do { + rcu_read_lock(); + policy = rcu_dereference(state->policy); + retval = __security_genfs_sid(policy, fstype, path, + orig_sclass, sid); + rcu_read_unlock(); + } while (retval == -ESTALE); return retval; +} + +int selinux_policy_genfs_sid(struct selinux_policy *policy, + const char *fstype, + char *path, + u16 orig_sclass, + u32 *sid) +{ + /* no lock required, policy is not yet accessible by other threads */ + return __security_genfs_sid(policy, fstype, path, orig_sclass, sid); } /** @@ -2795,17 +2954,26 @@ */ int security_fs_use(struct selinux_state *state, struct super_block *sb) { + struct selinux_policy *policy; struct policydb *policydb; struct sidtab *sidtab; - int rc = 0; + int rc; struct ocontext *c; struct superblock_security_struct *sbsec = sb->s_security; const char *fstype = sb->s_type->name; - read_lock(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) { + sbsec->behavior = SECURITY_FS_USE_NONE; + sbsec->sid = SECINITSID_UNLABELED; + return 0; + } - policydb = &state->ss->policydb; - sidtab = state->ss->sidtab; +retry: + rc = 0; + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; c = policydb->ocontexts[OCON_FSUSE]; while (c) { @@ -2816,16 +2984,20 @@ if (c) { sbsec->behavior = c->v.behavior; - if (!c->sid[0]) { - rc = context_struct_to_sid(state, &c->context[0], - &c->sid[0]); - if (rc) - goto out; + rc = ocontext_to_sid(sidtab, c, 0, &sbsec->sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + goto retry; } - sbsec->sid = c->sid[0]; + if (rc) + goto out; } else { - rc = __security_genfs_sid(state, fstype, "/", SECCLASS_DIR, - &sbsec->sid); + rc = __security_genfs_sid(policy, fstype, "/", + SECCLASS_DIR, &sbsec->sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + goto retry; + } if (rc) { sbsec->behavior = SECURITY_FS_USE_NONE; rc = 0; @@ -2835,26 +3007,18 @@ } out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } -int security_get_bools(struct selinux_state *state, - int *len, char ***names, int **values) +int security_get_bools(struct selinux_policy *policy, + u32 *len, char ***names, int **values) { struct policydb *policydb; - int i, rc; + u32 i; + int rc; - if (!state->initialized) { - *len = 0; - *names = NULL; - *values = NULL; - return 0; - } - - read_lock(&state->ss->policy_rwlock); - - policydb = &state->ss->policydb; + policydb = &policy->policydb; *names = NULL; *values = NULL; @@ -2885,7 +3049,6 @@ } rc = 0; out: - read_unlock(&state->ss->policy_rwlock); return rc; err: if (*names) { @@ -2901,68 +3064,91 @@ } -int security_set_bools(struct selinux_state *state, int len, int *values) +int security_set_bools(struct selinux_state *state, u32 len, int *values) { - struct policydb *policydb; - int i, rc; - int lenp, seqno = 0; - struct cond_node *cur; + struct selinux_policy *newpolicy, *oldpolicy; + int rc; + u32 i, seqno = 0; - write_lock_irq(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) + return -EINVAL; - policydb = &state->ss->policydb; + oldpolicy = rcu_dereference_protected(state->policy, + lockdep_is_held(&state->policy_mutex)); - rc = -EFAULT; - lenp = policydb->p_bools.nprim; - if (len != lenp) - goto out; + /* Consistency check on number of booleans, should never fail */ + if (WARN_ON(len != oldpolicy->policydb.p_bools.nprim)) + return -EINVAL; + newpolicy = kmemdup(oldpolicy, sizeof(*newpolicy), GFP_KERNEL); + if (!newpolicy) + return -ENOMEM; + + /* + * Deep copy only the parts of the policydb that might be + * modified as a result of changing booleans. + */ + rc = cond_policydb_dup(&newpolicy->policydb, &oldpolicy->policydb); + if (rc) { + kfree(newpolicy); + return -ENOMEM; + } + + /* Update the boolean states in the copy */ for (i = 0; i < len; i++) { - if (!!values[i] != policydb->bool_val_to_struct[i]->state) { + int new_state = !!values[i]; + int old_state = newpolicy->policydb.bool_val_to_struct[i]->state; + + if (new_state != old_state) { audit_log(audit_context(), GFP_ATOMIC, AUDIT_MAC_CONFIG_CHANGE, "bool=%s val=%d old_val=%d auid=%u ses=%u", - sym_name(policydb, SYM_BOOLS, i), - !!values[i], - policydb->bool_val_to_struct[i]->state, + sym_name(&newpolicy->policydb, SYM_BOOLS, i), + new_state, + old_state, from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); + newpolicy->policydb.bool_val_to_struct[i]->state = new_state; } - if (values[i]) - policydb->bool_val_to_struct[i]->state = 1; - else - policydb->bool_val_to_struct[i]->state = 0; } - for (cur = policydb->cond_list; cur; cur = cur->next) { - rc = evaluate_cond_node(policydb, cur); - if (rc) - goto out; - } + /* Re-evaluate the conditional rules in the copy */ + evaluate_cond_nodes(&newpolicy->policydb); - seqno = ++state->ss->latest_granting; - rc = 0; -out: - write_unlock_irq(&state->ss->policy_rwlock); - if (!rc) { - avc_ss_reset(state->avc, seqno); - selnl_notify_policyload(seqno); - selinux_status_update_policyload(state, seqno); - selinux_xfrm_notify_policyload(); - } - return rc; + /* Set latest granting seqno for new policy */ + newpolicy->latest_granting = oldpolicy->latest_granting + 1; + seqno = newpolicy->latest_granting; + + /* Install the new policy */ + rcu_assign_pointer(state->policy, newpolicy); + + /* + * Free the conditional portions of the old policydb + * that were copied for the new policy, and the oldpolicy + * structure itself but not what it references. + */ + synchronize_rcu(); + selinux_policy_cond_free(oldpolicy); + + /* Notify others of the policy change */ + selinux_notify_policy_change(state, seqno); + return 0; } int security_get_bool_value(struct selinux_state *state, - int index) + u32 index) { + struct selinux_policy *policy; struct policydb *policydb; int rc; - int len; + u32 len; - read_lock(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) + return 0; - policydb = &state->ss->policydb; + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; rc = -EFAULT; len = policydb->p_bools.nprim; @@ -2971,31 +3157,28 @@ rc = policydb->bool_val_to_struct[index]->state; out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } -static int security_preserve_bools(struct selinux_state *state, - struct policydb *policydb) +static int security_preserve_bools(struct selinux_policy *oldpolicy, + struct selinux_policy *newpolicy) { - int rc, nbools = 0, *bvalues = NULL, i; + int rc, *bvalues = NULL; char **bnames = NULL; struct cond_bool_datum *booldatum; - struct cond_node *cur; + u32 i, nbools = 0; - rc = security_get_bools(state, &nbools, &bnames, &bvalues); + rc = security_get_bools(oldpolicy, &nbools, &bnames, &bvalues); if (rc) goto out; for (i = 0; i < nbools; i++) { - booldatum = hashtab_search(policydb->p_bools.table, bnames[i]); + booldatum = symtab_search(&newpolicy->policydb.p_bools, + bnames[i]); if (booldatum) booldatum->state = bvalues[i]; } - for (cur = policydb->cond_list; cur; cur = cur->next) { - rc = evaluate_cond_node(policydb, cur); - if (rc) - goto out; - } + evaluate_cond_nodes(&newpolicy->policydb); out: if (bnames) { @@ -3014,8 +3197,9 @@ int security_sid_mls_copy(struct selinux_state *state, u32 sid, u32 mls_sid, u32 *new_sid) { - struct policydb *policydb = &state->ss->policydb; - struct sidtab *sidtab = state->ss->sidtab; + struct selinux_policy *policy; + struct policydb *policydb; + struct sidtab *sidtab; struct context *context1; struct context *context2; struct context newcon; @@ -3023,15 +3207,24 @@ u32 len; int rc; - rc = 0; - if (!state->initialized || !policydb->mls_enabled) { + if (!selinux_initialized(state)) { *new_sid = sid; - goto out; + return 0; } +retry: + rc = 0; context_init(&newcon); - read_lock(&state->ss->policy_rwlock); + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; + + if (!policydb->mls_enabled) { + *new_sid = sid; + goto out_unlock; + } rc = -EINVAL; context1 = sidtab_search(sidtab, sid); @@ -3058,24 +3251,35 @@ /* Check the validity of the new context. */ if (!policydb_context_isvalid(policydb, &newcon)) { - rc = convert_context_handle_invalid_context(state, &newcon); + rc = convert_context_handle_invalid_context(state, policydb, + &newcon); if (rc) { if (!context_struct_to_string(policydb, &newcon, &s, &len)) { - audit_log(audit_context(), - GFP_ATOMIC, AUDIT_SELINUX_ERR, - "op=security_sid_mls_copy " - "invalid_context=%s", s); + struct audit_buffer *ab; + + ab = audit_log_start(audit_context(), + GFP_ATOMIC, + AUDIT_SELINUX_ERR); + audit_log_format(ab, + "op=security_sid_mls_copy invalid_context="); + /* don't record NUL with untrusted strings */ + audit_log_n_untrustedstring(ab, s, len - 1); + audit_log_end(ab); kfree(s); } goto out_unlock; } } - rc = context_struct_to_sid(state, &newcon, new_sid); + rc = sidtab_context_to_sid(sidtab, &newcon, new_sid); + if (rc == -ESTALE) { + rcu_read_unlock(); + context_destroy(&newcon); + goto retry; + } out_unlock: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); context_destroy(&newcon); -out: return rc; } @@ -3104,8 +3308,9 @@ u32 xfrm_sid, u32 *peer_sid) { - struct policydb *policydb = &state->ss->policydb; - struct sidtab *sidtab = state->ss->sidtab; + struct selinux_policy *policy; + struct policydb *policydb; + struct sidtab *sidtab; int rc; struct context *nlbl_ctx; struct context *xfrm_ctx; @@ -3127,15 +3332,23 @@ return 0; } + if (!selinux_initialized(state)) + return 0; + + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; + /* * We don't need to check initialized here since the only way both * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the * security server was initialized and state->initialized was true. */ - if (!policydb->mls_enabled) - return 0; - - read_lock(&state->ss->policy_rwlock); + if (!policydb->mls_enabled) { + rc = 0; + goto out; + } rc = -EINVAL; nlbl_ctx = sidtab_search(sidtab, nlbl_sid); @@ -3162,7 +3375,7 @@ * expressive */ *peer_sid = xfrm_sid; out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } @@ -3179,19 +3392,13 @@ return 0; } -int security_get_classes(struct selinux_state *state, +int security_get_classes(struct selinux_policy *policy, char ***classes, int *nclasses) { - struct policydb *policydb = &state->ss->policydb; + struct policydb *policydb; int rc; - if (!state->initialized) { - *nclasses = 0; - *classes = NULL; - return 0; - } - - read_lock(&state->ss->policy_rwlock); + policydb = &policy->policydb; rc = -ENOMEM; *nclasses = policydb->p_classes.nprim; @@ -3199,8 +3406,8 @@ if (!*classes) goto out; - rc = hashtab_map(policydb->p_classes.table, get_classes_callback, - *classes); + rc = hashtab_map(&policydb->p_classes.table, get_classes_callback, + *classes); if (rc) { int i; for (i = 0; i < *nclasses; i++) @@ -3209,7 +3416,6 @@ } out: - read_unlock(&state->ss->policy_rwlock); return rc; } @@ -3226,17 +3432,17 @@ return 0; } -int security_get_permissions(struct selinux_state *state, +int security_get_permissions(struct selinux_policy *policy, char *class, char ***perms, int *nperms) { - struct policydb *policydb = &state->ss->policydb; + struct policydb *policydb; int rc, i; struct class_datum *match; - read_lock(&state->ss->policy_rwlock); + policydb = &policy->policydb; rc = -EINVAL; - match = hashtab_search(policydb->p_classes.table, class); + match = symtab_search(&policydb->p_classes, class); if (!match) { pr_err("SELinux: %s: unrecognized class %s\n", __func__, class); @@ -3250,23 +3456,21 @@ goto out; if (match->comdatum) { - rc = hashtab_map(match->comdatum->permissions.table, - get_permissions_callback, *perms); + rc = hashtab_map(&match->comdatum->permissions.table, + get_permissions_callback, *perms); if (rc) goto err; } - rc = hashtab_map(match->permissions.table, get_permissions_callback, - *perms); + rc = hashtab_map(&match->permissions.table, get_permissions_callback, + *perms); if (rc) goto err; out: - read_unlock(&state->ss->policy_rwlock); return rc; err: - read_unlock(&state->ss->policy_rwlock); for (i = 0; i < *nperms; i++) kfree((*perms)[i]); kfree(*perms); @@ -3275,12 +3479,32 @@ int security_get_reject_unknown(struct selinux_state *state) { - return state->ss->policydb.reject_unknown; + struct selinux_policy *policy; + int value; + + if (!selinux_initialized(state)) + return 0; + + rcu_read_lock(); + policy = rcu_dereference(state->policy); + value = policy->policydb.reject_unknown; + rcu_read_unlock(); + return value; } int security_get_allow_unknown(struct selinux_state *state) { - return state->ss->policydb.allow_unknown; + struct selinux_policy *policy; + int value; + + if (!selinux_initialized(state)) + return 0; + + rcu_read_lock(); + policy = rcu_dereference(state->policy); + value = policy->policydb.allow_unknown; + rcu_read_unlock(); + return value; } /** @@ -3296,12 +3520,16 @@ int security_policycap_supported(struct selinux_state *state, unsigned int req_cap) { - struct policydb *policydb = &state->ss->policydb; + struct selinux_policy *policy; int rc; - read_lock(&state->ss->policy_rwlock); - rc = ebitmap_get_bit(&policydb->policycaps, req_cap); - read_unlock(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) + return 0; + + rcu_read_lock(); + policy = rcu_dereference(state->policy); + rc = ebitmap_get_bit(&policy->policydb.policycaps, req_cap); + rcu_read_unlock(); return rc; } @@ -3324,7 +3552,8 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) { struct selinux_state *state = &selinux_state; - struct policydb *policydb = &state->ss->policydb; + struct selinux_policy *policy; + struct policydb *policydb; struct selinux_audit_rule *tmprule; struct role_datum *roledatum; struct type_datum *typedatum; @@ -3334,7 +3563,7 @@ *rule = NULL; - if (!state->initialized) + if (!selinux_initialized(state)) return -EOPNOTSUPP; switch (field) { @@ -3367,15 +3596,17 @@ context_init(&tmprule->au_ctxt); - read_lock(&state->ss->policy_rwlock); + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; - tmprule->au_seqno = state->ss->latest_granting; + tmprule->au_seqno = policy->latest_granting; switch (field) { case AUDIT_SUBJ_USER: case AUDIT_OBJ_USER: rc = -EINVAL; - userdatum = hashtab_search(policydb->p_users.table, rulestr); + userdatum = symtab_search(&policydb->p_users, rulestr); if (!userdatum) goto out; tmprule->au_ctxt.user = userdatum->value; @@ -3383,7 +3614,7 @@ case AUDIT_SUBJ_ROLE: case AUDIT_OBJ_ROLE: rc = -EINVAL; - roledatum = hashtab_search(policydb->p_roles.table, rulestr); + roledatum = symtab_search(&policydb->p_roles, rulestr); if (!roledatum) goto out; tmprule->au_ctxt.role = roledatum->value; @@ -3391,7 +3622,7 @@ case AUDIT_SUBJ_TYPE: case AUDIT_OBJ_TYPE: rc = -EINVAL; - typedatum = hashtab_search(policydb->p_types.table, rulestr); + typedatum = symtab_search(&policydb->p_types, rulestr); if (!typedatum) goto out; tmprule->au_ctxt.type = typedatum->value; @@ -3408,7 +3639,7 @@ } rc = 0; out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); if (rc) { selinux_audit_rule_free(tmprule); @@ -3445,10 +3676,10 @@ return 0; } -int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, - struct audit_context *actx) +int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule) { struct selinux_state *state = &selinux_state; + struct selinux_policy *policy; struct context *ctxt; struct mls_level *level; struct selinux_audit_rule *rule = vrule; @@ -3459,14 +3690,19 @@ return -ENOENT; } - read_lock(&state->ss->policy_rwlock); + if (!selinux_initialized(state)) + return 0; - if (rule->au_seqno < state->ss->latest_granting) { + rcu_read_lock(); + + policy = rcu_dereference(state->policy); + + if (rule->au_seqno < policy->latest_granting) { match = -ESTALE; goto out; } - ctxt = sidtab_search(state->ss->sidtab, sid); + ctxt = sidtab_search(policy->sidtab, sid); if (unlikely(!ctxt)) { WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n", sid); @@ -3550,7 +3786,7 @@ } out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return match; } @@ -3628,18 +3864,24 @@ struct netlbl_lsm_secattr *secattr, u32 *sid) { - struct policydb *policydb = &state->ss->policydb; - struct sidtab *sidtab = state->ss->sidtab; + struct selinux_policy *policy; + struct policydb *policydb; + struct sidtab *sidtab; int rc; struct context *ctx; struct context ctx_new; - if (!state->initialized) { + if (!selinux_initialized(state)) { *sid = SECSID_NULL; return 0; } - read_lock(&state->ss->policy_rwlock); +retry: + rc = 0; + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; + sidtab = policy->sidtab; if (secattr->flags & NETLBL_SECATTR_CACHE) *sid = *(u32 *)secattr->cache->data; @@ -3662,25 +3904,26 @@ goto out; } rc = -EIDRM; - if (!mls_context_isvalid(policydb, &ctx_new)) - goto out_free; + if (!mls_context_isvalid(policydb, &ctx_new)) { + ebitmap_destroy(&ctx_new.range.level[0].cat); + goto out; + } - rc = context_struct_to_sid(state, &ctx_new, sid); + rc = sidtab_context_to_sid(sidtab, &ctx_new, sid); + ebitmap_destroy(&ctx_new.range.level[0].cat); + if (rc == -ESTALE) { + rcu_read_unlock(); + goto retry; + } if (rc) - goto out_free; + goto out; security_netlbl_cache_add(secattr, *sid); - - ebitmap_destroy(&ctx_new.range.level[0].cat); } else *sid = SECSID_NULL; - read_unlock(&state->ss->policy_rwlock); - return 0; -out_free: - ebitmap_destroy(&ctx_new.range.level[0].cat); out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } @@ -3697,17 +3940,20 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state, u32 sid, struct netlbl_lsm_secattr *secattr) { - struct policydb *policydb = &state->ss->policydb; + struct selinux_policy *policy; + struct policydb *policydb; int rc; struct context *ctx; - if (!state->initialized) + if (!selinux_initialized(state)) return 0; - read_lock(&state->ss->policy_rwlock); + rcu_read_lock(); + policy = rcu_dereference(state->policy); + policydb = &policy->policydb; rc = -ENOENT; - ctx = sidtab_search(state->ss->sidtab, sid); + ctx = sidtab_search(policy->sidtab, sid); if (ctx == NULL) goto out; @@ -3722,7 +3968,7 @@ mls_export_netlbl_lvl(policydb, ctx, secattr); rc = mls_export_netlbl_cat(policydb, ctx, secattr); out: - read_unlock(&state->ss->policy_rwlock); + rcu_read_unlock(); return rc; } #endif /* CONFIG_NETLABEL */ @@ -3736,15 +3982,16 @@ int security_read_policy(struct selinux_state *state, void **data, size_t *len) { - struct policydb *policydb = &state->ss->policydb; + struct selinux_policy *policy; int rc; struct policy_file fp; - if (!state->initialized) + policy = rcu_dereference_protected( + state->policy, lockdep_is_held(&state->policy_mutex)); + if (!policy) return -EINVAL; - *len = security_policydb_len(state); - + *len = policy->policydb.len; *data = vmalloc_user(*len); if (!*data) return -ENOMEM; @@ -3752,10 +3999,7 @@ fp.data = *data; fp.len = *len; - read_lock(&state->ss->policy_rwlock); - rc = policydb_write(policydb, &fp); - read_unlock(&state->ss->policy_rwlock); - + rc = policydb_write(&policy->policydb, &fp); if (rc) return rc; -- Gitblit v1.6.2