aboutsummaryrefslogtreecommitdiffstats
path: root/security/selinux/ss/conditional.c
diff options
context:
space:
mode:
authorStephen Smalley <stephen.smalley.work@gmail.com>2020-08-11 15:01:56 -0400
committerPaul Moore <paul@paul-moore.com>2020-08-17 21:00:33 -0400
commitc7c556f1e81bb9e09656ed6650d0c44c84b7c016 (patch)
treef59001467a93880927534c5bb484bfb72d918cdb /security/selinux/ss/conditional.c
parentselinux: move policy commit after updating selinuxfs (diff)
downloadlinux-dev-c7c556f1e81bb9e09656ed6650d0c44c84b7c016.tar.xz
linux-dev-c7c556f1e81bb9e09656ed6650d0c44c84b7c016.zip
selinux: refactor changing booleans
Refactor the logic for changing SELinux policy booleans in a similar manner to the refactoring of policy load, thereby reducing the size of the critical section when the policy write-lock is held and making it easier to convert the policy rwlock to RCU in the future. Instead of directly modifying the policydb in place, modify a copy and then swap it into place through a single pointer update. Only fully copy the portions of the policydb that are affected by boolean changes to avoid the full cost of a deep policydb copy. Introduce another level of indirection for the sidtab since changing booleans does not require updating the sidtab, unlike policy load. While we are here, create a common helper for notifying other kernel components and userspace of a policy change and call it from both security_set_bools() and selinux_policy_commit(). Based on an old (2004) patch by Kaigai Kohei [1] to convert the policy rwlock to RCU that was deferred at the time since it did not significantly improve performance and introduced complexity. Peter Enderborg later submitted a patch series to convert to RCU [2] that would have made changing booleans a much more expensive operation by requiring a full policydb_write();policydb_read(); sequence to deep copy the entire policydb and also had concerns regarding atomic allocations. This change is now simplified by the earlier work to encapsulate policy state in the selinux_policy struct and to refactor policy load. After this change, the last major obstacle to converting the policy rwlock to RCU is likely the sidtab live convert support. [1] https://lore.kernel.org/selinux/6e2f9128-e191-ebb3-0e87-74bfccb0767f@tycho.nsa.gov/ [2] https://lore.kernel.org/selinux/20180530141104.28569-1-peter.enderborg@sony.com/ Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com> Signed-off-by: Paul Moore <paul@paul-moore.com>
Diffstat (limited to 'security/selinux/ss/conditional.c')
-rw-r--r--security/selinux/ss/conditional.c156
1 files changed, 156 insertions, 0 deletions
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 5a47258c1d77..05c7a10e0aab 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -600,3 +600,159 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
services_compute_xperms_drivers(xperms, node);
}
}
+
+static int cond_dup_av_list(struct cond_av_list *new,
+ struct cond_av_list *orig,
+ struct avtab *avtab)
+{
+ struct avtab_node *avnode;
+ u32 i;
+
+ memset(new, 0, sizeof(*new));
+
+ new->nodes = kcalloc(orig->len, sizeof(*new->nodes), GFP_KERNEL);
+ if (!new->nodes)
+ return -ENOMEM;
+
+ for (i = 0; i < orig->len; i++) {
+ avnode = avtab_search_node(avtab, &orig->nodes[i]->key);
+ if (WARN_ON(!avnode))
+ return -EINVAL;
+ new->nodes[i] = avnode;
+ new->len++;
+ }
+
+ return 0;
+}
+
+static int duplicate_policydb_cond_list(struct policydb *newp,
+ struct policydb *origp)
+{
+ int rc, i, j;
+
+ rc = avtab_duplicate(&newp->te_cond_avtab, &origp->te_cond_avtab);
+ if (rc)
+ return rc;
+
+ newp->cond_list_len = 0;
+ newp->cond_list = kcalloc(origp->cond_list_len,
+ sizeof(*newp->cond_list),
+ GFP_KERNEL);
+ if (!newp->cond_list)
+ goto error;
+
+ for (i = 0; i < origp->cond_list_len; i++) {
+ struct cond_node *newn = &newp->cond_list[i];
+ struct cond_node *orign = &origp->cond_list[i];
+
+ newp->cond_list_len++;
+
+ newn->cur_state = orign->cur_state;
+ newn->expr.nodes = kcalloc(orign->expr.len,
+ sizeof(*newn->expr.nodes), GFP_KERNEL);
+ if (!newn->expr.nodes)
+ goto error;
+ for (j = 0; j < orign->expr.len; j++)
+ newn->expr.nodes[j] = orign->expr.nodes[j];
+ newn->expr.len = orign->expr.len;
+
+ rc = cond_dup_av_list(&newn->true_list, &orign->true_list,
+ &newp->te_cond_avtab);
+ if (rc)
+ goto error;
+
+ rc = cond_dup_av_list(&newn->false_list, &orign->false_list,
+ &newp->te_cond_avtab);
+ if (rc)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ avtab_destroy(&newp->te_cond_avtab);
+ cond_list_destroy(newp);
+ return -ENOMEM;
+}
+
+static int cond_bools_destroy(void *key, void *datum, void *args)
+{
+ /* key was not copied so no need to free here */
+ kfree(datum);
+ return 0;
+}
+
+static int cond_bools_copy(struct hashtab_node *new, struct hashtab_node *orig, void *args)
+{
+ struct cond_bool_datum *datum;
+
+ datum = kmalloc(sizeof(struct cond_bool_datum), GFP_KERNEL);
+ if (!datum)
+ return -ENOMEM;
+
+ memcpy(datum, orig->datum, sizeof(struct cond_bool_datum));
+
+ new->key = orig->key; /* No need to copy, never modified */
+ new->datum = datum;
+ return 0;
+}
+
+static int cond_bools_index(void *key, void *datum, void *args)
+{
+ struct cond_bool_datum *booldatum, **cond_bool_array;
+
+ booldatum = datum;
+ cond_bool_array = args;
+ cond_bool_array[booldatum->value - 1] = booldatum;
+
+ return 0;
+}
+
+static int duplicate_policydb_bools(struct policydb *newdb,
+ struct policydb *orig)
+{
+ struct cond_bool_datum **cond_bool_array;
+ int rc;
+
+ cond_bool_array = kmalloc_array(orig->p_bools.nprim,
+ sizeof(*orig->bool_val_to_struct),
+ GFP_KERNEL);
+ if (!cond_bool_array)
+ return -ENOMEM;
+
+ rc = hashtab_duplicate(&newdb->p_bools.table, &orig->p_bools.table,
+ cond_bools_copy, cond_bools_destroy, NULL);
+ if (rc) {
+ kfree(cond_bool_array);
+ return -ENOMEM;
+ }
+
+ hashtab_map(&newdb->p_bools.table, cond_bools_index, cond_bool_array);
+ newdb->bool_val_to_struct = cond_bool_array;
+
+ newdb->p_bools.nprim = orig->p_bools.nprim;
+
+ return 0;
+}
+
+void cond_policydb_destroy_dup(struct policydb *p)
+{
+ hashtab_map(&p->p_bools.table, cond_bools_destroy, NULL);
+ hashtab_destroy(&p->p_bools.table);
+ cond_policydb_destroy(p);
+}
+
+int cond_policydb_dup(struct policydb *new, struct policydb *orig)
+{
+ cond_policydb_init(new);
+
+ if (duplicate_policydb_bools(new, orig))
+ return -ENOMEM;
+
+ if (duplicate_policydb_cond_list(new, orig)) {
+ cond_policydb_destroy_dup(new);
+ return -ENOMEM;
+ }
+
+ return 0;
+}