diff options
-rw-r--r-- | security/selinux/ss/mls.c | 24 | ||||
-rw-r--r-- | security/selinux/ss/mls.h | 3 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 122 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.c | 563 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.h | 80 |
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 | */ |
442 | int mls_convert_context(struct policydb *oldp, | 443 | int 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 | ||
47 | int mls_convert_context(struct policydb *oldp, | 47 | int 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 | ||
51 | int mls_compute_sid(struct policydb *p, | 52 | int 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 | */ |
1915 | static int convert_context(u32 key, | 1916 | static 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; | ||
2036 | out: | ||
2037 | return rc; | ||
2038 | bad: | 2018 | bad: |
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 | ||
2053 | static void security_load_policycaps(struct selinux_state *state) | 2031 | static 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 | |||
18 | int sidtab_init(struct sidtab *s) | 20 | int 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 | ||
42 | static 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 | |||
87 | int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context) | 40 | int 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 | ||
105 | static struct context *sidtab_lookup(struct sidtab *s, u32 sid) | 58 | static 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 | |||
70 | static 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 | |||
91 | static 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); | 131 | static 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 | ||
121 | static struct context *sidtab_lookup_initial(struct sidtab *s, u32 sid) | 144 | static 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 | ||
155 | static int sidtab_map(struct sidtab *s, | 175 | static 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 | } |
176 | out: | 207 | return -ENOENT; |
177 | return rc; | ||
178 | } | 208 | } |
179 | 209 | ||
180 | /* Clone the SID into the new SID table. */ | 210 | static void sidtab_rcache_update(struct sidtab *s, u32 index, u32 pos) |
181 | static 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 | ||
187 | int sidtab_convert(struct sidtab *s, struct sidtab *news, | 219 | static 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 | ||
207 | static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc) | 224 | static 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 | ||
218 | static 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 | ||
238 | static 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 | ||
257 | static int sidtab_reverse_lookup(struct sidtab *s, struct context *context, | 244 | static 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) |
285 | unlock_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; | ||
334 | out_unlock: | ||
335 | spin_unlock_irqrestore(&s->lock, flags); | ||
336 | return rc; | ||
290 | } | 337 | } |
291 | 338 | ||
292 | int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid) | 339 | int 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 | ||
313 | void sidtab_hash_eval(struct sidtab *h, char *tag) | 360 | static 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 | |||
407 | int 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(¶ms->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(¶ms->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 | ||
340 | void sidtab_destroy(struct sidtab *s) | 457 | static 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 | |||
482 | void 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 | ||
13 | struct sidtab_node { | 19 | struct 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 | |
23 | struct sidtab_node_inner; | ||
24 | struct sidtab_node_leaf; | ||
25 | |||
26 | union 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 | |||
50 | struct sidtab_node_leaf { | ||
51 | struct sidtab_entry_leaf entries[SIDTAB_LEAF_ENTRIES]; | ||
52 | }; | ||
53 | |||
54 | struct sidtab_node_inner { | ||
55 | union sidtab_entry_inner entries[SIDTAB_INNER_ENTRIES]; | ||
56 | }; | ||
24 | 57 | ||
25 | struct sidtab_isid_entry { | 58 | struct sidtab_isid_entry { |
26 | int set; | 59 | int set; |
27 | struct context context; | 60 | struct context context; |
28 | }; | 61 | }; |
29 | 62 | ||
63 | struct 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 | |||
30 | struct sidtab { | 71 | struct 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); | |||
45 | struct context *sidtab_search(struct sidtab *s, u32 sid); | 86 | struct context *sidtab_search(struct sidtab *s, u32 sid); |
46 | struct context *sidtab_search_force(struct sidtab *s, u32 sid); | 87 | struct context *sidtab_search_force(struct sidtab *s, u32 sid); |
47 | 88 | ||
48 | int sidtab_convert(struct sidtab *s, struct sidtab *news, | 89 | int 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 | ||
54 | int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid); | 91 | int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid); |
55 | 92 | ||
56 | void sidtab_hash_eval(struct sidtab *h, char *tag); | ||
57 | void sidtab_destroy(struct sidtab *s); | 93 | void sidtab_destroy(struct sidtab *s); |
58 | 94 | ||
59 | #endif /* _SS_SIDTAB_H_ */ | 95 | #endif /* _SS_SIDTAB_H_ */ |