aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--security/selinux/ss/mls.c24
-rw-r--r--security/selinux/ss/mls.h3
-rw-r--r--security/selinux/ss/services.c122
-rw-r--r--security/selinux/ss/sidtab.c563
-rw-r--r--security/selinux/ss/sidtab.h80
5 files changed, 468 insertions, 324 deletions
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 2fe459df3c85..18ba0c2328fb 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -436,16 +436,17 @@ int mls_setup_user_range(struct policydb *p,
436 436
437/* 437/*
438 * Convert the MLS fields in the security context 438 * Convert the MLS fields in the security context
439 * structure `c' from the values specified in the 439 * structure `oldc' from the values specified in the
440 * policy `oldp' to the values specified in the policy `newp'. 440 * policy `oldp' to the values specified in the policy `newp',
441 * storing the resulting context in `newc'.
441 */ 442 */
442int mls_convert_context(struct policydb *oldp, 443int mls_convert_context(struct policydb *oldp,
443 struct policydb *newp, 444 struct policydb *newp,
444 struct context *c) 445 struct context *oldc,
446 struct context *newc)
445{ 447{
446 struct level_datum *levdatum; 448 struct level_datum *levdatum;
447 struct cat_datum *catdatum; 449 struct cat_datum *catdatum;
448 struct ebitmap bitmap;
449 struct ebitmap_node *node; 450 struct ebitmap_node *node;
450 int l, i; 451 int l, i;
451 452
@@ -455,28 +456,25 @@ int mls_convert_context(struct policydb *oldp,
455 for (l = 0; l < 2; l++) { 456 for (l = 0; l < 2; l++) {
456 levdatum = hashtab_search(newp->p_levels.table, 457 levdatum = hashtab_search(newp->p_levels.table,
457 sym_name(oldp, SYM_LEVELS, 458 sym_name(oldp, SYM_LEVELS,
458 c->range.level[l].sens - 1)); 459 oldc->range.level[l].sens - 1));
459 460
460 if (!levdatum) 461 if (!levdatum)
461 return -EINVAL; 462 return -EINVAL;
462 c->range.level[l].sens = levdatum->level->sens; 463 newc->range.level[l].sens = levdatum->level->sens;
463 464
464 ebitmap_init(&bitmap); 465 ebitmap_for_each_positive_bit(&oldc->range.level[l].cat,
465 ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) { 466 node, i) {
466 int rc; 467 int rc;
467 468
468 catdatum = hashtab_search(newp->p_cats.table, 469 catdatum = hashtab_search(newp->p_cats.table,
469 sym_name(oldp, SYM_CATS, i)); 470 sym_name(oldp, SYM_CATS, i));
470 if (!catdatum) 471 if (!catdatum)
471 return -EINVAL; 472 return -EINVAL;
472 rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); 473 rc = ebitmap_set_bit(&newc->range.level[l].cat,
474 catdatum->value - 1, 1);
473 if (rc) 475 if (rc)
474 return rc; 476 return rc;
475
476 cond_resched();
477 } 477 }
478 ebitmap_destroy(&c->range.level[l].cat);
479 c->range.level[l].cat = bitmap;
480 } 478 }
481 479
482 return 0; 480 return 0;
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 67093647576d..7954b1e60b64 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -46,7 +46,8 @@ int mls_range_set(struct context *context, struct mls_range *range);
46 46
47int mls_convert_context(struct policydb *oldp, 47int mls_convert_context(struct policydb *oldp,
48 struct policydb *newp, 48 struct policydb *newp,
49 struct context *context); 49 struct context *oldc,
50 struct context *newc);
50 51
51int mls_compute_sid(struct policydb *p, 52int mls_compute_sid(struct policydb *p,
52 struct context *scontext, 53 struct context *scontext,
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 4ff4b0edbf6b..dd44126c8d14 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1907,19 +1907,16 @@ struct convert_context_args {
1907 1907
1908/* 1908/*
1909 * Convert the values in the security context 1909 * Convert the values in the security context
1910 * structure `c' from the values specified 1910 * structure `oldc' from the values specified
1911 * in the policy `p->oldp' to the values specified 1911 * in the policy `p->oldp' to the values specified
1912 * in the policy `p->newp'. Verify that the 1912 * in the policy `p->newp', storing the new context
1913 * context is valid under the new policy. 1913 * in `newc'. Verify that the context is valid
1914 * under the new policy.
1914 */ 1915 */
1915static int convert_context(u32 key, 1916static int convert_context(struct context *oldc, struct context *newc, void *p)
1916 struct context *c,
1917 void *p)
1918{ 1917{
1919 struct convert_context_args *args; 1918 struct convert_context_args *args;
1920 struct context oldc;
1921 struct ocontext *oc; 1919 struct ocontext *oc;
1922 struct mls_range *range;
1923 struct role_datum *role; 1920 struct role_datum *role;
1924 struct type_datum *typdatum; 1921 struct type_datum *typdatum;
1925 struct user_datum *usrdatum; 1922 struct user_datum *usrdatum;
@@ -1929,76 +1926,65 @@ static int convert_context(u32 key,
1929 1926
1930 args = p; 1927 args = p;
1931 1928
1932 if (c->str) { 1929 if (oldc->str) {
1933 struct context ctx; 1930 s = kstrdup(oldc->str, GFP_KERNEL);
1934
1935 rc = -ENOMEM;
1936 s = kstrdup(c->str, GFP_KERNEL);
1937 if (!s) 1931 if (!s)
1938 goto out; 1932 return -ENOMEM;
1939 1933
1940 rc = string_to_context_struct(args->newp, NULL, s, 1934 rc = string_to_context_struct(args->newp, NULL, s,
1941 &ctx, SECSID_NULL); 1935 newc, SECSID_NULL);
1942 kfree(s); 1936 if (rc == -EINVAL) {
1943 if (!rc) {
1944 pr_info("SELinux: Context %s became valid (mapped).\n",
1945 c->str);
1946 /* Replace string with mapped representation. */
1947 kfree(c->str);
1948 memcpy(c, &ctx, sizeof(*c));
1949 goto out;
1950 } else if (rc == -EINVAL) {
1951 /* Retain string representation for later mapping. */ 1937 /* Retain string representation for later mapping. */
1952 rc = 0; 1938 context_init(newc);
1953 goto out; 1939 newc->str = s;
1954 } else { 1940 newc->len = oldc->len;
1941 return 0;
1942 }
1943 kfree(s);
1944 if (rc) {
1955 /* Other error condition, e.g. ENOMEM. */ 1945 /* Other error condition, e.g. ENOMEM. */
1956 pr_err("SELinux: Unable to map context %s, rc = %d.\n", 1946 pr_err("SELinux: Unable to map context %s, rc = %d.\n",
1957 c->str, -rc); 1947 oldc->str, -rc);
1958 goto out; 1948 return rc;
1959 } 1949 }
1950 pr_info("SELinux: Context %s became valid (mapped).\n",
1951 oldc->str);
1952 return 0;
1960 } 1953 }
1961 1954
1962 rc = context_cpy(&oldc, c); 1955 context_init(newc);
1963 if (rc)
1964 goto out;
1965 1956
1966 /* Convert the user. */ 1957 /* Convert the user. */
1967 rc = -EINVAL; 1958 rc = -EINVAL;
1968 usrdatum = hashtab_search(args->newp->p_users.table, 1959 usrdatum = hashtab_search(args->newp->p_users.table,
1969 sym_name(args->oldp, SYM_USERS, c->user - 1)); 1960 sym_name(args->oldp,
1961 SYM_USERS, oldc->user - 1));
1970 if (!usrdatum) 1962 if (!usrdatum)
1971 goto bad; 1963 goto bad;
1972 c->user = usrdatum->value; 1964 newc->user = usrdatum->value;
1973 1965
1974 /* Convert the role. */ 1966 /* Convert the role. */
1975 rc = -EINVAL; 1967 rc = -EINVAL;
1976 role = hashtab_search(args->newp->p_roles.table, 1968 role = hashtab_search(args->newp->p_roles.table,
1977 sym_name(args->oldp, SYM_ROLES, c->role - 1)); 1969 sym_name(args->oldp, SYM_ROLES, oldc->role - 1));
1978 if (!role) 1970 if (!role)
1979 goto bad; 1971 goto bad;
1980 c->role = role->value; 1972 newc->role = role->value;
1981 1973
1982 /* Convert the type. */ 1974 /* Convert the type. */
1983 rc = -EINVAL; 1975 rc = -EINVAL;
1984 typdatum = hashtab_search(args->newp->p_types.table, 1976 typdatum = hashtab_search(args->newp->p_types.table,
1985 sym_name(args->oldp, SYM_TYPES, c->type - 1)); 1977 sym_name(args->oldp,
1978 SYM_TYPES, oldc->type - 1));
1986 if (!typdatum) 1979 if (!typdatum)
1987 goto bad; 1980 goto bad;
1988 c->type = typdatum->value; 1981 newc->type = typdatum->value;
1989 1982
1990 /* Convert the MLS fields if dealing with MLS policies */ 1983 /* Convert the MLS fields if dealing with MLS policies */
1991 if (args->oldp->mls_enabled && args->newp->mls_enabled) { 1984 if (args->oldp->mls_enabled && args->newp->mls_enabled) {
1992 rc = mls_convert_context(args->oldp, args->newp, c); 1985 rc = mls_convert_context(args->oldp, args->newp, oldc, newc);
1993 if (rc) 1986 if (rc)
1994 goto bad; 1987 goto bad;
1995 } else if (args->oldp->mls_enabled && !args->newp->mls_enabled) {
1996 /*
1997 * Switching between MLS and non-MLS policy:
1998 * free any storage used by the MLS fields in the
1999 * context for all existing entries in the sidtab.
2000 */
2001 mls_context_destroy(c);
2002 } else if (!args->oldp->mls_enabled && args->newp->mls_enabled) { 1988 } else if (!args->oldp->mls_enabled && args->newp->mls_enabled) {
2003 /* 1989 /*
2004 * Switching between non-MLS and MLS policy: 1990 * Switching between non-MLS and MLS policy:
@@ -2016,38 +2002,30 @@ static int convert_context(u32 key,
2016 " the initial SIDs list\n"); 2002 " the initial SIDs list\n");
2017 goto bad; 2003 goto bad;
2018 } 2004 }
2019 range = &oc->context[0].range; 2005 rc = mls_range_set(newc, &oc->context[0].range);
2020 rc = mls_range_set(c, range);
2021 if (rc) 2006 if (rc)
2022 goto bad; 2007 goto bad;
2023 } 2008 }
2024 2009
2025 /* Check the validity of the new context. */ 2010 /* Check the validity of the new context. */
2026 if (!policydb_context_isvalid(args->newp, c)) { 2011 if (!policydb_context_isvalid(args->newp, newc)) {
2027 rc = convert_context_handle_invalid_context(args->state, 2012 rc = convert_context_handle_invalid_context(args->state, oldc);
2028 &oldc);
2029 if (rc) 2013 if (rc)
2030 goto bad; 2014 goto bad;
2031 } 2015 }
2032 2016
2033 context_destroy(&oldc); 2017 return 0;
2034
2035 rc = 0;
2036out:
2037 return rc;
2038bad: 2018bad:
2039 /* Map old representation to string and save it. */ 2019 /* Map old representation to string and save it. */
2040 rc = context_struct_to_string(args->oldp, &oldc, &s, &len); 2020 rc = context_struct_to_string(args->oldp, oldc, &s, &len);
2041 if (rc) 2021 if (rc)
2042 return rc; 2022 return rc;
2043 context_destroy(&oldc); 2023 context_destroy(newc);
2044 context_destroy(c); 2024 newc->str = s;
2045 c->str = s; 2025 newc->len = len;
2046 c->len = len;
2047 pr_info("SELinux: Context %s became invalid (unmapped).\n", 2026 pr_info("SELinux: Context %s became invalid (unmapped).\n",
2048 c->str); 2027 newc->str);
2049 rc = 0; 2028 return 0;
2050 goto out;
2051} 2029}
2052 2030
2053static void security_load_policycaps(struct selinux_state *state) 2031static void security_load_policycaps(struct selinux_state *state)
@@ -2091,6 +2069,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
2091 struct policydb *oldpolicydb, *newpolicydb; 2069 struct policydb *oldpolicydb, *newpolicydb;
2092 struct selinux_mapping *oldmapping; 2070 struct selinux_mapping *oldmapping;
2093 struct selinux_map newmap; 2071 struct selinux_map newmap;
2072 struct sidtab_convert_params convert_params;
2094 struct convert_context_args args; 2073 struct convert_context_args args;
2095 u32 seqno; 2074 u32 seqno;
2096 int rc = 0; 2075 int rc = 0;
@@ -2147,12 +2126,6 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
2147 goto out; 2126 goto out;
2148 } 2127 }
2149 2128
2150 oldsidtab = state->ss->sidtab;
2151
2152#if 0
2153 sidtab_hash_eval(oldsidtab, "sids");
2154#endif
2155
2156 rc = policydb_read(newpolicydb, fp); 2129 rc = policydb_read(newpolicydb, fp);
2157 if (rc) { 2130 if (rc) {
2158 kfree(newsidtab); 2131 kfree(newsidtab);
@@ -2184,6 +2157,8 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
2184 goto err; 2157 goto err;
2185 } 2158 }
2186 2159
2160 oldsidtab = state->ss->sidtab;
2161
2187 /* 2162 /*
2188 * Convert the internal representations of contexts 2163 * Convert the internal representations of contexts
2189 * in the new SID table. 2164 * in the new SID table.
@@ -2191,7 +2166,12 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
2191 args.state = state; 2166 args.state = state;
2192 args.oldp = policydb; 2167 args.oldp = policydb;
2193 args.newp = newpolicydb; 2168 args.newp = newpolicydb;
2194 rc = sidtab_convert(oldsidtab, newsidtab, convert_context, &args); 2169
2170 convert_params.func = convert_context;
2171 convert_params.args = &args;
2172 convert_params.target = newsidtab;
2173
2174 rc = sidtab_convert(oldsidtab, &convert_params);
2195 if (rc) { 2175 if (rc) {
2196 pr_err("SELinux: unable to convert the internal" 2176 pr_err("SELinux: unable to convert the internal"
2197 " representation of contexts in the new SID" 2177 " representation of contexts in the new SID"
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index e44e7cec630c..e63a90ff2728 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -2,88 +2,41 @@
2/* 2/*
3 * Implementation of the SID table type. 3 * Implementation of the SID table type.
4 * 4 *
5 * Author : Stephen Smalley, <sds@tycho.nsa.gov> 5 * Original author: Stephen Smalley, <sds@tycho.nsa.gov>
6 * Author: Ondrej Mosnacek, <omosnacek@gmail.com>
7 *
8 * Copyright (C) 2018 Red Hat, Inc.
6 */ 9 */
10#include <linux/errno.h>
7#include <linux/kernel.h> 11#include <linux/kernel.h>
8#include <linux/slab.h> 12#include <linux/slab.h>
13#include <linux/sched.h>
9#include <linux/spinlock.h> 14#include <linux/spinlock.h>
10#include <linux/errno.h> 15#include <linux/atomic.h>
11#include "flask.h" 16#include "flask.h"
12#include "security.h" 17#include "security.h"
13#include "sidtab.h" 18#include "sidtab.h"
14 19
15#define SIDTAB_HASH(sid) \
16(sid & SIDTAB_HASH_MASK)
17
18int sidtab_init(struct sidtab *s) 20int sidtab_init(struct sidtab *s)
19{ 21{
20 int i; 22 u32 i;
21 23
22 s->htable = kmalloc_array(SIDTAB_SIZE, sizeof(*s->htable), GFP_ATOMIC); 24 memset(s->roots, 0, sizeof(s->roots));
23 if (!s->htable) 25
24 return -ENOMEM; 26 for (i = 0; i < SIDTAB_RCACHE_SIZE; i++)
27 atomic_set(&s->rcache[i], -1);
25 28
26 for (i = 0; i < SECINITSID_NUM; i++) 29 for (i = 0; i < SECINITSID_NUM; i++)
27 s->isids[i].set = 0; 30 s->isids[i].set = 0;
28 31
29 for (i = 0; i < SIDTAB_SIZE; i++) 32 atomic_set(&s->count, 0);
30 s->htable[i] = NULL;
31 33
32 for (i = 0; i < SIDTAB_CACHE_LEN; i++) 34 s->convert = NULL;
33 s->cache[i] = NULL;
34 35
35 s->nel = 0;
36 s->next_sid = 0;
37 s->shutdown = 0;
38 spin_lock_init(&s->lock); 36 spin_lock_init(&s->lock);
39 return 0; 37 return 0;
40} 38}
41 39
42static int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
43{
44 int hvalue;
45 struct sidtab_node *prev, *cur, *newnode;
46
47 if (!s)
48 return -ENOMEM;
49
50 hvalue = SIDTAB_HASH(sid);
51 prev = NULL;
52 cur = s->htable[hvalue];
53 while (cur && sid > cur->sid) {
54 prev = cur;
55 cur = cur->next;
56 }
57
58 if (cur && sid == cur->sid)
59 return -EEXIST;
60
61 newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
62 if (!newnode)
63 return -ENOMEM;
64
65 newnode->sid = sid;
66 if (context_cpy(&newnode->context, context)) {
67 kfree(newnode);
68 return -ENOMEM;
69 }
70
71 if (prev) {
72 newnode->next = prev->next;
73 wmb();
74 prev->next = newnode;
75 } else {
76 newnode->next = s->htable[hvalue];
77 wmb();
78 s->htable[hvalue] = newnode;
79 }
80
81 s->nel++;
82 if (sid >= s->next_sid)
83 s->next_sid = sid + 1;
84 return 0;
85}
86
87int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context) 40int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
88{ 41{
89 struct sidtab_isid_entry *entry; 42 struct sidtab_isid_entry *entry;
@@ -102,20 +55,90 @@ int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
102 return 0; 55 return 0;
103} 56}
104 57
105static struct context *sidtab_lookup(struct sidtab *s, u32 sid) 58static u32 sidtab_level_from_count(u32 count)
106{ 59{
107 int hvalue; 60 u32 capacity = SIDTAB_LEAF_ENTRIES;
108 struct sidtab_node *cur; 61 u32 level = 0;
62
63 while (count > capacity) {
64 capacity <<= SIDTAB_INNER_SHIFT;
65 ++level;
66 }
67 return level;
68}
69
70static int sidtab_alloc_roots(struct sidtab *s, u32 level)
71{
72 u32 l;
73
74 if (!s->roots[0].ptr_leaf) {
75 s->roots[0].ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
76 GFP_ATOMIC);
77 if (!s->roots[0].ptr_leaf)
78 return -ENOMEM;
79 }
80 for (l = 1; l <= level; ++l)
81 if (!s->roots[l].ptr_inner) {
82 s->roots[l].ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
83 GFP_ATOMIC);
84 if (!s->roots[l].ptr_inner)
85 return -ENOMEM;
86 s->roots[l].ptr_inner->entries[0] = s->roots[l - 1];
87 }
88 return 0;
89}
90
91static struct context *sidtab_do_lookup(struct sidtab *s, u32 index, int alloc)
92{
93 union sidtab_entry_inner *entry;
94 u32 level, capacity_shift, leaf_index = index / SIDTAB_LEAF_ENTRIES;
95
96 /* find the level of the subtree we need */
97 level = sidtab_level_from_count(index + 1);
98 capacity_shift = level * SIDTAB_INNER_SHIFT;
99
100 /* allocate roots if needed */
101 if (alloc && sidtab_alloc_roots(s, level) != 0)
102 return NULL;
103
104 /* lookup inside the subtree */
105 entry = &s->roots[level];
106 while (level != 0) {
107 capacity_shift -= SIDTAB_INNER_SHIFT;
108 --level;
109
110 entry = &entry->ptr_inner->entries[leaf_index >> capacity_shift];
111 leaf_index &= ((u32)1 << capacity_shift) - 1;
112
113 if (!entry->ptr_inner) {
114 if (alloc)
115 entry->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
116 GFP_ATOMIC);
117 if (!entry->ptr_inner)
118 return NULL;
119 }
120 }
121 if (!entry->ptr_leaf) {
122 if (alloc)
123 entry->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
124 GFP_ATOMIC);
125 if (!entry->ptr_leaf)
126 return NULL;
127 }
128 return &entry->ptr_leaf->entries[index % SIDTAB_LEAF_ENTRIES].context;
129}
109 130
110 hvalue = SIDTAB_HASH(sid); 131static struct context *sidtab_lookup(struct sidtab *s, u32 index)
111 cur = s->htable[hvalue]; 132{
112 while (cur && sid > cur->sid) 133 u32 count = (u32)atomic_read(&s->count);
113 cur = cur->next;
114 134
115 if (!cur || sid != cur->sid) 135 if (index >= count)
116 return NULL; 136 return NULL;
117 137
118 return &cur->context; 138 /* read entries after reading count */
139 smp_rmb();
140
141 return sidtab_do_lookup(s, index, 0);
119} 142}
120 143
121static struct context *sidtab_lookup_initial(struct sidtab *s, u32 sid) 144static struct context *sidtab_lookup_initial(struct sidtab *s, u32 sid)
@@ -127,9 +150,6 @@ static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
127{ 150{
128 struct context *context; 151 struct context *context;
129 152
130 if (!s)
131 return NULL;
132
133 if (sid != 0) { 153 if (sid != 0) {
134 if (sid > SECINITSID_NUM) 154 if (sid > SECINITSID_NUM)
135 context = sidtab_lookup(s, sid - (SECINITSID_NUM + 1)); 155 context = sidtab_lookup(s, sid - (SECINITSID_NUM + 1));
@@ -152,102 +172,69 @@ struct context *sidtab_search_force(struct sidtab *s, u32 sid)
152 return sidtab_search_core(s, sid, 1); 172 return sidtab_search_core(s, sid, 1);
153} 173}
154 174
155static int sidtab_map(struct sidtab *s, 175static int sidtab_find_context(union sidtab_entry_inner entry,
156 int (*apply)(u32 sid, 176 u32 *pos, u32 count, u32 level,
157 struct context *context, 177 struct context *context, u32 *index)
158 void *args),
159 void *args)
160{ 178{
161 int i, rc = 0; 179 int rc;
162 struct sidtab_node *cur; 180 u32 i;
163 181
164 if (!s) 182 if (level != 0) {
165 goto out; 183 struct sidtab_node_inner *node = entry.ptr_inner;
166 184
167 for (i = 0; i < SIDTAB_SIZE; i++) { 185 i = 0;
168 cur = s->htable[i]; 186 while (i < SIDTAB_INNER_ENTRIES && *pos < count) {
169 while (cur) { 187 rc = sidtab_find_context(node->entries[i],
170 rc = apply(cur->sid, &cur->context, args); 188 pos, count, level - 1,
171 if (rc) 189 context, index);
172 goto out; 190 if (rc == 0)
173 cur = cur->next; 191 return 0;
192 i++;
193 }
194 } else {
195 struct sidtab_node_leaf *node = entry.ptr_leaf;
196
197 i = 0;
198 while (i < SIDTAB_LEAF_ENTRIES && *pos < count) {
199 if (context_cmp(&node->entries[i].context, context)) {
200 *index = *pos;
201 return 0;
202 }
203 (*pos)++;
204 i++;
174 } 205 }
175 } 206 }
176out: 207 return -ENOENT;
177 return rc;
178} 208}
179 209
180/* Clone the SID into the new SID table. */ 210static void sidtab_rcache_update(struct sidtab *s, u32 index, u32 pos)
181static int clone_sid(u32 sid, struct context *context, void *arg)
182{ 211{
183 struct sidtab *s = arg; 212 while (pos > 0) {
184 return sidtab_insert(s, sid, context); 213 atomic_set(&s->rcache[pos], atomic_read(&s->rcache[pos - 1]));
214 --pos;
215 }
216 atomic_set(&s->rcache[0], (int)index);
185} 217}
186 218
187int sidtab_convert(struct sidtab *s, struct sidtab *news, 219static void sidtab_rcache_push(struct sidtab *s, u32 index)
188 int (*convert)(u32 sid,
189 struct context *context,
190 void *args),
191 void *args)
192{ 220{
193 unsigned long flags; 221 sidtab_rcache_update(s, index, SIDTAB_RCACHE_SIZE - 1);
194 int rc;
195
196 spin_lock_irqsave(&s->lock, flags);
197 s->shutdown = 1;
198 spin_unlock_irqrestore(&s->lock, flags);
199
200 rc = sidtab_map(s, clone_sid, news);
201 if (rc)
202 return rc;
203
204 return sidtab_map(news, convert, args);
205} 222}
206 223
207static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc) 224static int sidtab_rcache_search(struct sidtab *s, struct context *context,
225 u32 *index)
208{ 226{
209 BUG_ON(loc >= SIDTAB_CACHE_LEN); 227 u32 i;
210 228
211 while (loc > 0) { 229 for (i = 0; i < SIDTAB_RCACHE_SIZE; i++) {
212 s->cache[loc] = s->cache[loc - 1]; 230 int v = atomic_read(&s->rcache[i]);
213 loc--;
214 }
215 s->cache[0] = n;
216}
217 231
218static inline int sidtab_search_context(struct sidtab *s, 232 if (v < 0)
219 struct context *context, u32 *sid) 233 continue;
220{
221 int i;
222 struct sidtab_node *cur;
223
224 for (i = 0; i < SIDTAB_SIZE; i++) {
225 cur = s->htable[i];
226 while (cur) {
227 if (context_cmp(&cur->context, context)) {
228 sidtab_update_cache(s, cur, SIDTAB_CACHE_LEN - 1);
229 *sid = cur->sid;
230 return 0;
231 }
232 cur = cur->next;
233 }
234 }
235 return -ENOENT;
236}
237 234
238static inline int sidtab_search_cache(struct sidtab *s, struct context *context, 235 if (context_cmp(sidtab_do_lookup(s, (u32)v, 0), context)) {
239 u32 *sid) 236 sidtab_rcache_update(s, (u32)v, i);
240{ 237 *index = (u32)v;
241 int i;
242 struct sidtab_node *node;
243
244 for (i = 0; i < SIDTAB_CACHE_LEN; i++) {
245 node = s->cache[i];
246 if (unlikely(!node))
247 return -ENOENT;
248 if (context_cmp(&node->context, context)) {
249 sidtab_update_cache(s, node, i);
250 *sid = node->sid;
251 return 0; 238 return 0;
252 } 239 }
253 } 240 }
@@ -255,38 +242,98 @@ static inline int sidtab_search_cache(struct sidtab *s, struct context *context,
255} 242}
256 243
257static int sidtab_reverse_lookup(struct sidtab *s, struct context *context, 244static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
258 u32 *sid) 245 u32 *index)
259{ 246{
260 int ret;
261 unsigned long flags; 247 unsigned long flags;
248 u32 count = (u32)atomic_read(&s->count);
249 u32 count_locked, level, pos;
250 struct sidtab_convert_params *convert;
251 struct context *dst, *dst_convert;
252 int rc;
262 253
263 ret = sidtab_search_cache(s, context, sid); 254 rc = sidtab_rcache_search(s, context, index);
264 if (ret) 255 if (rc == 0)
265 ret = sidtab_search_context(s, context, sid); 256 return 0;
266 if (ret) { 257
267 spin_lock_irqsave(&s->lock, flags); 258 level = sidtab_level_from_count(count);
268 /* Rescan now that we hold the lock. */ 259
269 ret = sidtab_search_context(s, context, sid); 260 /* read entries after reading count */
270 if (!ret) 261 smp_rmb();
271 goto unlock_out; 262
272 /* No SID exists for the context. Allocate a new one. */ 263 pos = 0;
273 if (s->next_sid == (UINT_MAX - SECINITSID_NUM - 1) || 264 rc = sidtab_find_context(s->roots[level], &pos, count, level,
274 s->shutdown) { 265 context, index);
275 ret = -ENOMEM; 266 if (rc == 0) {
276 goto unlock_out; 267 sidtab_rcache_push(s, *index);
268 return 0;
269 }
270
271 /* lock-free search failed: lock, re-search, and insert if not found */
272 spin_lock_irqsave(&s->lock, flags);
273
274 convert = s->convert;
275 count_locked = (u32)atomic_read(&s->count);
276 level = sidtab_level_from_count(count_locked);
277
278 /* if count has changed before we acquired the lock, then catch up */
279 while (count < count_locked) {
280 if (context_cmp(sidtab_do_lookup(s, count, 0), context)) {
281 sidtab_rcache_push(s, count);
282 *index = count;
283 rc = 0;
284 goto out_unlock;
277 } 285 }
278 *sid = s->next_sid++; 286 ++count;
279 if (context->len) 287 }
280 pr_info("SELinux: Context %s is not valid (left unmapped).\n", 288
281 context->str); 289 /* insert context into new entry */
282 ret = sidtab_insert(s, *sid, context); 290 rc = -ENOMEM;
283 if (ret) 291 dst = sidtab_do_lookup(s, count, 1);
284 s->next_sid--; 292 if (!dst)
285unlock_out: 293 goto out_unlock;
286 spin_unlock_irqrestore(&s->lock, flags); 294
295 rc = context_cpy(dst, context);
296 if (rc)
297 goto out_unlock;
298
299 /*
300 * if we are building a new sidtab, we need to convert the context
301 * and insert it there as well
302 */
303 if (convert) {
304 rc = -ENOMEM;
305 dst_convert = sidtab_do_lookup(convert->target, count, 1);
306 if (!dst_convert) {
307 context_destroy(dst);
308 goto out_unlock;
309 }
310
311 rc = convert->func(context, dst_convert, convert->args);
312 if (rc) {
313 context_destroy(dst);
314 goto out_unlock;
315 }
316
317 /* at this point we know the insert won't fail */
318 atomic_set(&convert->target->count, count + 1);
287 } 319 }
288 320
289 return ret; 321 if (context->len)
322 pr_info("SELinux: Context %s is not valid (left unmapped).\n",
323 context->str);
324
325 sidtab_rcache_push(s, count);
326 *index = count;
327
328 /* write entries before writing new count */
329 smp_wmb();
330
331 atomic_set(&s->count, count + 1);
332
333 rc = 0;
334out_unlock:
335 spin_unlock_irqrestore(&s->lock, flags);
336 return rc;
290} 337}
291 338
292int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid) 339int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid)
@@ -310,57 +357,139 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid)
310 return 0; 357 return 0;
311} 358}
312 359
313void sidtab_hash_eval(struct sidtab *h, char *tag) 360static int sidtab_convert_tree(union sidtab_entry_inner *edst,
361 union sidtab_entry_inner *esrc,
362 u32 *pos, u32 count, u32 level,
363 struct sidtab_convert_params *convert)
314{ 364{
315 int i, chain_len, slots_used, max_chain_len; 365 int rc;
316 struct sidtab_node *cur; 366 u32 i;
317
318 slots_used = 0;
319 max_chain_len = 0;
320 for (i = 0; i < SIDTAB_SIZE; i++) {
321 cur = h->htable[i];
322 if (cur) {
323 slots_used++;
324 chain_len = 0;
325 while (cur) {
326 chain_len++;
327 cur = cur->next;
328 }
329 367
330 if (chain_len > max_chain_len) 368 if (level != 0) {
331 max_chain_len = chain_len; 369 if (!edst->ptr_inner) {
370 edst->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
371 GFP_KERNEL);
372 if (!edst->ptr_inner)
373 return -ENOMEM;
332 } 374 }
375 i = 0;
376 while (i < SIDTAB_INNER_ENTRIES && *pos < count) {
377 rc = sidtab_convert_tree(&edst->ptr_inner->entries[i],
378 &esrc->ptr_inner->entries[i],
379 pos, count, level - 1,
380 convert);
381 if (rc)
382 return rc;
383 i++;
384 }
385 } else {
386 if (!edst->ptr_leaf) {
387 edst->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
388 GFP_KERNEL);
389 if (!edst->ptr_leaf)
390 return -ENOMEM;
391 }
392 i = 0;
393 while (i < SIDTAB_LEAF_ENTRIES && *pos < count) {
394 rc = convert->func(&esrc->ptr_leaf->entries[i].context,
395 &edst->ptr_leaf->entries[i].context,
396 convert->args);
397 if (rc)
398 return rc;
399 (*pos)++;
400 i++;
401 }
402 cond_resched();
403 }
404 return 0;
405}
406
407int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
408{
409 unsigned long flags;
410 u32 count, level, pos;
411 int rc;
412
413 spin_lock_irqsave(&s->lock, flags);
414
415 /* concurrent policy loads are not allowed */
416 if (s->convert) {
417 spin_unlock_irqrestore(&s->lock, flags);
418 return -EBUSY;
333 } 419 }
334 420
335 pr_debug("%s: %d entries and %d/%d buckets used, longest " 421 count = (u32)atomic_read(&s->count);
336 "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE, 422 level = sidtab_level_from_count(count);
337 max_chain_len); 423
424 /* allocate last leaf in the new sidtab (to avoid race with
425 * live convert)
426 */
427 rc = sidtab_do_lookup(params->target, count - 1, 1) ? 0 : -ENOMEM;
428 if (rc) {
429 spin_unlock_irqrestore(&s->lock, flags);
430 return rc;
431 }
432
433 /* set count in case no new entries are added during conversion */
434 atomic_set(&params->target->count, count);
435
436 /* enable live convert of new entries */
437 s->convert = params;
438
439 /* we can safely do the rest of the conversion outside the lock */
440 spin_unlock_irqrestore(&s->lock, flags);
441
442 pr_info("SELinux: Converting %u SID table entries...\n", count);
443
444 /* convert all entries not covered by live convert */
445 pos = 0;
446 rc = sidtab_convert_tree(&params->target->roots[level],
447 &s->roots[level], &pos, count, level, params);
448 if (rc) {
449 /* we need to keep the old table - disable live convert */
450 spin_lock_irqsave(&s->lock, flags);
451 s->convert = NULL;
452 spin_unlock_irqrestore(&s->lock, flags);
453 }
454 return rc;
338} 455}
339 456
340void sidtab_destroy(struct sidtab *s) 457static void sidtab_destroy_tree(union sidtab_entry_inner entry, u32 level)
341{ 458{
342 int i; 459 u32 i;
343 struct sidtab_node *cur, *temp; 460
461 if (level != 0) {
462 struct sidtab_node_inner *node = entry.ptr_inner;
463
464 if (!node)
465 return;
466
467 for (i = 0; i < SIDTAB_INNER_ENTRIES; i++)
468 sidtab_destroy_tree(node->entries[i], level - 1);
469 kfree(node);
470 } else {
471 struct sidtab_node_leaf *node = entry.ptr_leaf;
344 472
345 if (!s) 473 if (!node)
346 return; 474 return;
475
476 for (i = 0; i < SIDTAB_LEAF_ENTRIES; i++)
477 context_destroy(&node->entries[i].context);
478 kfree(node);
479 }
480}
481
482void sidtab_destroy(struct sidtab *s)
483{
484 u32 i, level;
347 485
348 for (i = 0; i < SECINITSID_NUM; i++) 486 for (i = 0; i < SECINITSID_NUM; i++)
349 if (s->isids[i].set) 487 if (s->isids[i].set)
350 context_destroy(&s->isids[i].context); 488 context_destroy(&s->isids[i].context);
351 489
352 for (i = 0; i < SIDTAB_SIZE; i++) { 490 level = SIDTAB_MAX_LEVEL;
353 cur = s->htable[i]; 491 while (level && !s->roots[level].ptr_inner)
354 while (cur) { 492 --level;
355 temp = cur; 493
356 cur = cur->next; 494 sidtab_destroy_tree(s->roots[level], level);
357 context_destroy(&temp->context);
358 kfree(temp);
359 }
360 s->htable[i] = NULL;
361 }
362 kfree(s->htable);
363 s->htable = NULL;
364 s->nel = 0;
365 s->next_sid = 1;
366} 495}
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
index e657ae6bf996..bbd5c0d1f3bd 100644
--- a/security/selinux/ss/sidtab.h
+++ b/security/selinux/ss/sidtab.h
@@ -1,41 +1,82 @@
1/* SPDX-License-Identifier: GPL-2.0 */ 1/* SPDX-License-Identifier: GPL-2.0 */
2/* 2/*
3 * A security identifier table (sidtab) is a hash table 3 * A security identifier table (sidtab) is a lookup table
4 * of security context structures indexed by SID value. 4 * of security context structures indexed by SID value.
5 * 5 *
6 * Author : Stephen Smalley, <sds@tycho.nsa.gov> 6 * Original author: Stephen Smalley, <sds@tycho.nsa.gov>
7 * Author: Ondrej Mosnacek, <omosnacek@gmail.com>
8 *
9 * Copyright (C) 2018 Red Hat, Inc.
7 */ 10 */
8#ifndef _SS_SIDTAB_H_ 11#ifndef _SS_SIDTAB_H_
9#define _SS_SIDTAB_H_ 12#define _SS_SIDTAB_H_
10 13
14#include <linux/spinlock_types.h>
15#include <linux/log2.h>
16
11#include "context.h" 17#include "context.h"
12 18
13struct sidtab_node { 19struct sidtab_entry_leaf {
14 u32 sid; /* security identifier */ 20 struct context context;
15 struct context context; /* security context structure */ 21};
16 struct sidtab_node *next; 22
23struct sidtab_node_inner;
24struct sidtab_node_leaf;
25
26union sidtab_entry_inner {
27 struct sidtab_node_inner *ptr_inner;
28 struct sidtab_node_leaf *ptr_leaf;
17}; 29};
18 30
19#define SIDTAB_HASH_BITS 7 31/* align node size to page boundary */
20#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS) 32#define SIDTAB_NODE_ALLOC_SHIFT PAGE_SHIFT
21#define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1) 33#define SIDTAB_NODE_ALLOC_SIZE PAGE_SIZE
34
35#define size_to_shift(size) ((size) == 1 ? 1 : (const_ilog2((size) - 1) + 1))
36
37#define SIDTAB_INNER_SHIFT \
38 (SIDTAB_NODE_ALLOC_SHIFT - size_to_shift(sizeof(union sidtab_entry_inner)))
39#define SIDTAB_INNER_ENTRIES ((size_t)1 << SIDTAB_INNER_SHIFT)
40#define SIDTAB_LEAF_ENTRIES \
41 (SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry_leaf))
22 42
23#define SIDTAB_SIZE SIDTAB_HASH_BUCKETS 43#define SIDTAB_MAX_BITS 31 /* limited to INT_MAX due to atomic_t range */
44#define SIDTAB_MAX (((u32)1 << SIDTAB_MAX_BITS) - 1)
45/* ensure enough tree levels for SIDTAB_MAX entries */
46#define SIDTAB_MAX_LEVEL \
47 DIV_ROUND_UP(SIDTAB_MAX_BITS - size_to_shift(SIDTAB_LEAF_ENTRIES), \
48 SIDTAB_INNER_SHIFT)
49
50struct sidtab_node_leaf {
51 struct sidtab_entry_leaf entries[SIDTAB_LEAF_ENTRIES];
52};
53
54struct sidtab_node_inner {
55 union sidtab_entry_inner entries[SIDTAB_INNER_ENTRIES];
56};
24 57
25struct sidtab_isid_entry { 58struct sidtab_isid_entry {
26 int set; 59 int set;
27 struct context context; 60 struct context context;
28}; 61};
29 62
63struct sidtab_convert_params {
64 int (*func)(struct context *oldc, struct context *newc, void *args);
65 void *args;
66 struct sidtab *target;
67};
68
69#define SIDTAB_RCACHE_SIZE 3
70
30struct sidtab { 71struct sidtab {
31 struct sidtab_node **htable; 72 union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1];
32 unsigned int nel; /* number of elements */ 73 atomic_t count;
33 unsigned int next_sid; /* next SID to allocate */ 74 struct sidtab_convert_params *convert;
34 unsigned char shutdown;
35#define SIDTAB_CACHE_LEN 3
36 struct sidtab_node *cache[SIDTAB_CACHE_LEN];
37 spinlock_t lock; 75 spinlock_t lock;
38 76
77 /* reverse lookup cache */
78 atomic_t rcache[SIDTAB_RCACHE_SIZE];
79
39 /* index == SID - 1 (no entry for SECSID_NULL) */ 80 /* index == SID - 1 (no entry for SECSID_NULL) */
40 struct sidtab_isid_entry isids[SECINITSID_NUM]; 81 struct sidtab_isid_entry isids[SECINITSID_NUM];
41}; 82};
@@ -45,15 +86,10 @@ int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context);
45struct context *sidtab_search(struct sidtab *s, u32 sid); 86struct context *sidtab_search(struct sidtab *s, u32 sid);
46struct context *sidtab_search_force(struct sidtab *s, u32 sid); 87struct context *sidtab_search_force(struct sidtab *s, u32 sid);
47 88
48int sidtab_convert(struct sidtab *s, struct sidtab *news, 89int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);
49 int (*apply)(u32 sid,
50 struct context *context,
51 void *args),
52 void *args);
53 90
54int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid); 91int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid);
55 92
56void sidtab_hash_eval(struct sidtab *h, char *tag);
57void sidtab_destroy(struct sidtab *s); 93void sidtab_destroy(struct sidtab *s);
58 94
59#endif /* _SS_SIDTAB_H_ */ 95#endif /* _SS_SIDTAB_H_ */