blob: 98d5ea3fcde4cdd27cc3b165721d240a7cf65ac4 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * Implementation of the SID table type.
4 *
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +01005 * Original author: Stephen Smalley, <sds@tycho.nsa.gov>
6 * Author: Ondrej Mosnacek, <omosnacek@gmail.com>
7 *
8 * Copyright (C) 2018 Red Hat, Inc.
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +010010#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/kernel.h>
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +010012#include <linux/list.h>
13#include <linux/rcupdate.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/slab.h>
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +010015#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/spinlock.h>
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +020017#include <asm/barrier.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include "flask.h"
19#include "security.h"
20#include "sidtab.h"
21
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +010022struct sidtab_str_cache {
23 struct rcu_head rcu_member;
24 struct list_head lru_member;
25 struct sidtab_entry *parent;
26 u32 len;
27 char str[];
28};
29
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010030#define index_to_sid(index) (index + SECINITSID_NUM + 1)
31#define sid_to_index(sid) (sid - (SECINITSID_NUM + 1))
32
Linus Torvalds1da177e2005-04-16 15:20:36 -070033int sidtab_init(struct sidtab *s)
34{
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +010035 u32 i;
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +010037 memset(s->roots, 0, sizeof(s->roots));
38
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010039 for (i = 0; i < SECINITSID_NUM; i++)
40 s->isids[i].set = 0;
41
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +020042 s->count = 0;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +010043 s->convert = NULL;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010044 hash_init(s->context_to_sid);
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010045
James Morrisbdd581c2008-06-06 18:50:12 +100046 spin_lock_init(&s->lock);
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +010047
48#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
49 s->cache_free_slots = CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE;
50 INIT_LIST_HEAD(&s->cache_lru_list);
51 spin_lock_init(&s->cache_lock);
52#endif
53
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 return 0;
55}
56
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010057static u32 context_to_sid(struct sidtab *s, struct context *context)
58{
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +010059 struct sidtab_entry *entry;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010060 u32 sid = 0;
61
62 rcu_read_lock();
63 hash_for_each_possible_rcu(s->context_to_sid, entry, list,
64 context->hash) {
65 if (context_cmp(&entry->context, context)) {
66 sid = entry->sid;
67 break;
68 }
69 }
70 rcu_read_unlock();
71 return sid;
72}
73
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010074int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
75{
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +010076 struct sidtab_isid_entry *isid;
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010077 int rc;
78
79 if (sid == 0 || sid > SECINITSID_NUM)
80 return -EINVAL;
81
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +010082 isid = &s->isids[sid - 1];
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010083
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +010084 rc = context_cpy(&isid->entry.context, context);
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010085 if (rc)
86 return rc;
87
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +010088#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
89 isid->entry.cache = NULL;
90#endif
91 isid->set = 1;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010092
93 /*
94 * Multiple initial sids may map to the same context. Check that this
95 * context is not already represented in the context_to_sid hashtable
96 * to avoid duplicate entries and long linked lists upon hash
97 * collision.
98 */
99 if (!context_to_sid(s, context)) {
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100100 isid->entry.sid = sid;
101 hash_add(s->context_to_sid, &isid->entry.list, context->hash);
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100102 }
103
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100104 return 0;
105}
106
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100107int sidtab_hash_stats(struct sidtab *sidtab, char *page)
108{
109 int i;
110 int chain_len = 0;
111 int slots_used = 0;
112 int entries = 0;
113 int max_chain_len = 0;
114 int cur_bucket = 0;
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100115 struct sidtab_entry *entry;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100116
117 rcu_read_lock();
118 hash_for_each_rcu(sidtab->context_to_sid, i, entry, list) {
119 entries++;
120 if (i == cur_bucket) {
121 chain_len++;
122 if (chain_len == 1)
123 slots_used++;
124 } else {
125 cur_bucket = i;
126 if (chain_len > max_chain_len)
127 max_chain_len = chain_len;
128 chain_len = 0;
129 }
130 }
131 rcu_read_unlock();
132
133 if (chain_len > max_chain_len)
134 max_chain_len = chain_len;
135
136 return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
137 "longest chain: %d\n", entries,
138 slots_used, SIDTAB_HASH_BUCKETS, max_chain_len);
139}
140
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100141static u32 sidtab_level_from_count(u32 count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100143 u32 capacity = SIDTAB_LEAF_ENTRIES;
144 u32 level = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100146 while (count > capacity) {
147 capacity <<= SIDTAB_INNER_SHIFT;
148 ++level;
149 }
150 return level;
151}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100153static int sidtab_alloc_roots(struct sidtab *s, u32 level)
154{
155 u32 l;
156
157 if (!s->roots[0].ptr_leaf) {
158 s->roots[0].ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
159 GFP_ATOMIC);
160 if (!s->roots[0].ptr_leaf)
161 return -ENOMEM;
162 }
163 for (l = 1; l <= level; ++l)
164 if (!s->roots[l].ptr_inner) {
165 s->roots[l].ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
166 GFP_ATOMIC);
167 if (!s->roots[l].ptr_inner)
168 return -ENOMEM;
169 s->roots[l].ptr_inner->entries[0] = s->roots[l - 1];
170 }
171 return 0;
172}
173
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100174static struct sidtab_entry *sidtab_do_lookup(struct sidtab *s, u32 index,
175 int alloc)
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100176{
177 union sidtab_entry_inner *entry;
178 u32 level, capacity_shift, leaf_index = index / SIDTAB_LEAF_ENTRIES;
179
180 /* find the level of the subtree we need */
181 level = sidtab_level_from_count(index + 1);
182 capacity_shift = level * SIDTAB_INNER_SHIFT;
183
184 /* allocate roots if needed */
185 if (alloc && sidtab_alloc_roots(s, level) != 0)
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100186 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100188 /* lookup inside the subtree */
189 entry = &s->roots[level];
190 while (level != 0) {
191 capacity_shift -= SIDTAB_INNER_SHIFT;
192 --level;
193
194 entry = &entry->ptr_inner->entries[leaf_index >> capacity_shift];
195 leaf_index &= ((u32)1 << capacity_shift) - 1;
196
197 if (!entry->ptr_inner) {
198 if (alloc)
199 entry->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
200 GFP_ATOMIC);
201 if (!entry->ptr_inner)
202 return NULL;
203 }
204 }
205 if (!entry->ptr_leaf) {
206 if (alloc)
207 entry->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
208 GFP_ATOMIC);
209 if (!entry->ptr_leaf)
210 return NULL;
211 }
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100212 return &entry->ptr_leaf->entries[index % SIDTAB_LEAF_ENTRIES];
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100213}
214
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100215static struct sidtab_entry *sidtab_lookup(struct sidtab *s, u32 index)
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100216{
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +0200217 /* read entries only after reading count */
218 u32 count = smp_load_acquire(&s->count);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100219
220 if (index >= count)
221 return NULL;
222
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100223 return sidtab_do_lookup(s, index, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224}
225
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100226static struct sidtab_entry *sidtab_lookup_initial(struct sidtab *s, u32 sid)
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100227{
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100228 return s->isids[sid - 1].set ? &s->isids[sid - 1].entry : NULL;
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100229}
230
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100231static struct sidtab_entry *sidtab_search_core(struct sidtab *s, u32 sid,
232 int force)
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100233{
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100234 if (sid != 0) {
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100235 struct sidtab_entry *entry;
236
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100237 if (sid > SECINITSID_NUM)
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100238 entry = sidtab_lookup(s, sid_to_index(sid));
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100239 else
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100240 entry = sidtab_lookup_initial(s, sid);
241 if (entry && (!entry->context.len || force))
242 return entry;
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100243 }
244
245 return sidtab_lookup_initial(s, SECINITSID_UNLABELED);
246}
247
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100248struct sidtab_entry *sidtab_search_entry(struct sidtab *s, u32 sid)
Stephen Smalley12b29f32008-05-07 13:03:20 -0400249{
250 return sidtab_search_core(s, sid, 0);
251}
252
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100253struct sidtab_entry *sidtab_search_entry_force(struct sidtab *s, u32 sid)
Stephen Smalley12b29f32008-05-07 13:03:20 -0400254{
255 return sidtab_search_core(s, sid, 1);
256}
257
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100258int sidtab_context_to_sid(struct sidtab *s, struct context *context,
259 u32 *sid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 unsigned long flags;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100262 u32 count;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100263 struct sidtab_convert_params *convert;
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100264 struct sidtab_entry *dst, *dst_convert;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100265 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100267 *sid = context_to_sid(s, context);
268 if (*sid)
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100269 return 0;
270
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100271 /* lock-free search failed: lock, re-search, and insert if not found */
272 spin_lock_irqsave(&s->lock, flags);
273
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100274 rc = 0;
275 *sid = context_to_sid(s, context);
276 if (*sid)
277 goto out_unlock;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100278
Ondrej Mosnacek433e3aa2020-04-08 11:08:08 +0200279 count = s->count;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100280 convert = s->convert;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100281
Ondrej Mosnacekacbc3722019-07-23 08:50:59 +0200282 /* bail out if we already reached max entries */
283 rc = -EOVERFLOW;
284 if (count >= SIDTAB_MAX)
285 goto out_unlock;
286
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100287 /* insert context into new entry */
288 rc = -ENOMEM;
289 dst = sidtab_do_lookup(s, count, 1);
290 if (!dst)
291 goto out_unlock;
292
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100293 dst->sid = index_to_sid(count);
294
295 rc = context_cpy(&dst->context, context);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100296 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) {
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100307 context_destroy(&dst->context);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100308 goto out_unlock;
309 }
310
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100311 rc = convert->func(context, &dst_convert->context,
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100312 convert->args);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100313 if (rc) {
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100314 context_destroy(&dst->context);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100315 goto out_unlock;
316 }
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100317 dst_convert->sid = index_to_sid(count);
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +0200318 convert->target->count = count + 1;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100319
320 hash_add_rcu(convert->target->context_to_sid,
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100321 &dst_convert->list, dst_convert->context.hash);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100322 }
323
324 if (context->len)
325 pr_info("SELinux: Context %s is not valid (left unmapped).\n",
326 context->str);
327
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100328 *sid = index_to_sid(count);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100329
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100330 /* write entries before updating count */
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +0200331 smp_store_release(&s->count, count + 1);
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100332 hash_add_rcu(s->context_to_sid, &dst->list, dst->context.hash);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100333
334 rc = 0;
335out_unlock:
336 spin_unlock_irqrestore(&s->lock, flags);
337 return rc;
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100338}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100340static void sidtab_convert_hashtable(struct sidtab *s, u32 count)
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100341{
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100342 struct sidtab_entry *entry;
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100343 u32 i;
344
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100345 for (i = 0; i < count; i++) {
346 entry = sidtab_do_lookup(s, i, 0);
347 entry->sid = index_to_sid(i);
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100348
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100349 hash_add_rcu(s->context_to_sid, &entry->list,
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100350 entry->context.hash);
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100351
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100352 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353}
354
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100355static int sidtab_convert_tree(union sidtab_entry_inner *edst,
356 union sidtab_entry_inner *esrc,
357 u32 *pos, u32 count, u32 level,
358 struct sidtab_convert_params *convert)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359{
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100360 int rc;
361 u32 i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100363 if (level != 0) {
364 if (!edst->ptr_inner) {
365 edst->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
366 GFP_KERNEL);
367 if (!edst->ptr_inner)
368 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 }
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100370 i = 0;
371 while (i < SIDTAB_INNER_ENTRIES && *pos < count) {
372 rc = sidtab_convert_tree(&edst->ptr_inner->entries[i],
373 &esrc->ptr_inner->entries[i],
374 pos, count, level - 1,
375 convert);
376 if (rc)
377 return rc;
378 i++;
379 }
380 } else {
381 if (!edst->ptr_leaf) {
382 edst->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
383 GFP_KERNEL);
384 if (!edst->ptr_leaf)
385 return -ENOMEM;
386 }
387 i = 0;
388 while (i < SIDTAB_LEAF_ENTRIES && *pos < count) {
389 rc = convert->func(&esrc->ptr_leaf->entries[i].context,
390 &edst->ptr_leaf->entries[i].context,
391 convert->args);
392 if (rc)
393 return rc;
394 (*pos)++;
395 i++;
396 }
397 cond_resched();
398 }
399 return 0;
400}
401
402int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
403{
404 unsigned long flags;
405 u32 count, level, pos;
406 int rc;
407
408 spin_lock_irqsave(&s->lock, flags);
409
410 /* concurrent policy loads are not allowed */
411 if (s->convert) {
412 spin_unlock_irqrestore(&s->lock, flags);
413 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 }
415
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +0200416 count = s->count;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100417 level = sidtab_level_from_count(count);
418
419 /* allocate last leaf in the new sidtab (to avoid race with
420 * live convert)
421 */
422 rc = sidtab_do_lookup(params->target, count - 1, 1) ? 0 : -ENOMEM;
423 if (rc) {
424 spin_unlock_irqrestore(&s->lock, flags);
425 return rc;
426 }
427
428 /* set count in case no new entries are added during conversion */
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +0200429 params->target->count = count;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100430
431 /* enable live convert of new entries */
432 s->convert = params;
433
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100434 /* we can safely convert the tree outside the lock */
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100435 spin_unlock_irqrestore(&s->lock, flags);
436
437 pr_info("SELinux: Converting %u SID table entries...\n", count);
438
439 /* convert all entries not covered by live convert */
440 pos = 0;
441 rc = sidtab_convert_tree(&params->target->roots[level],
442 &s->roots[level], &pos, count, level, params);
443 if (rc) {
444 /* we need to keep the old table - disable live convert */
445 spin_lock_irqsave(&s->lock, flags);
446 s->convert = NULL;
447 spin_unlock_irqrestore(&s->lock, flags);
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100448 return rc;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100449 }
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100450 /*
451 * The hashtable can also be modified in sidtab_context_to_sid()
452 * so we must re-acquire the lock here.
453 */
454 spin_lock_irqsave(&s->lock, flags);
455 sidtab_convert_hashtable(params->target, count);
456 spin_unlock_irqrestore(&s->lock, flags);
457
458 return 0;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100459}
460
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100461static void sidtab_destroy_entry(struct sidtab_entry *entry)
462{
463 context_destroy(&entry->context);
464#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
465 kfree(rcu_dereference_raw(entry->cache));
466#endif
467}
468
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100469static void sidtab_destroy_tree(union sidtab_entry_inner entry, u32 level)
470{
471 u32 i;
472
473 if (level != 0) {
474 struct sidtab_node_inner *node = entry.ptr_inner;
475
476 if (!node)
477 return;
478
479 for (i = 0; i < SIDTAB_INNER_ENTRIES; i++)
480 sidtab_destroy_tree(node->entries[i], level - 1);
481 kfree(node);
482 } else {
483 struct sidtab_node_leaf *node = entry.ptr_leaf;
484
485 if (!node)
486 return;
487
488 for (i = 0; i < SIDTAB_LEAF_ENTRIES; i++)
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100489 sidtab_destroy_entry(&node->entries[i]);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100490 kfree(node);
491 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492}
493
494void sidtab_destroy(struct sidtab *s)
495{
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100496 u32 i, level;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100498 for (i = 0; i < SECINITSID_NUM; i++)
499 if (s->isids[i].set)
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100500 sidtab_destroy_entry(&s->isids[i].entry);
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100501
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100502 level = SIDTAB_MAX_LEVEL;
503 while (level && !s->roots[level].ptr_inner)
504 --level;
505
506 sidtab_destroy_tree(s->roots[level], level);
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100507 /*
508 * The context_to_sid hashtable's objects are all shared
509 * with the isids array and context tree, and so don't need
510 * to be cleaned up here.
511 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512}
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100513
514#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
515
516void sidtab_sid2str_put(struct sidtab *s, struct sidtab_entry *entry,
517 const char *str, u32 str_len)
518{
519 struct sidtab_str_cache *cache, *victim = NULL;
Ondrej Mosnacek39a706f2020-02-03 09:50:23 +0100520 unsigned long flags;
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100521
522 /* do not cache invalid contexts */
523 if (entry->context.len)
524 return;
525
Ondrej Mosnacek39a706f2020-02-03 09:50:23 +0100526 spin_lock_irqsave(&s->cache_lock, flags);
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100527
528 cache = rcu_dereference_protected(entry->cache,
529 lockdep_is_held(&s->cache_lock));
530 if (cache) {
531 /* entry in cache - just bump to the head of LRU list */
532 list_move(&cache->lru_member, &s->cache_lru_list);
533 goto out_unlock;
534 }
535
536 cache = kmalloc(sizeof(struct sidtab_str_cache) + str_len, GFP_ATOMIC);
537 if (!cache)
538 goto out_unlock;
539
540 if (s->cache_free_slots == 0) {
541 /* pop a cache entry from the tail and free it */
542 victim = container_of(s->cache_lru_list.prev,
543 struct sidtab_str_cache, lru_member);
544 list_del(&victim->lru_member);
545 rcu_assign_pointer(victim->parent->cache, NULL);
546 } else {
547 s->cache_free_slots--;
548 }
549 cache->parent = entry;
550 cache->len = str_len;
551 memcpy(cache->str, str, str_len);
552 list_add(&cache->lru_member, &s->cache_lru_list);
553
554 rcu_assign_pointer(entry->cache, cache);
555
556out_unlock:
Ondrej Mosnacek39a706f2020-02-03 09:50:23 +0100557 spin_unlock_irqrestore(&s->cache_lock, flags);
Ondrej Mosnacekd97bd232019-11-26 14:57:00 +0100558 kfree_rcu(victim, rcu_member);
559}
560
561int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry,
562 char **out, u32 *out_len)
563{
564 struct sidtab_str_cache *cache;
565 int rc = 0;
566
567 if (entry->context.len)
568 return -ENOENT; /* do not cache invalid contexts */
569
570 rcu_read_lock();
571
572 cache = rcu_dereference(entry->cache);
573 if (!cache) {
574 rc = -ENOENT;
575 } else {
576 *out_len = cache->len;
577 if (out) {
578 *out = kmemdup(cache->str, cache->len, GFP_ATOMIC);
579 if (!*out)
580 rc = -ENOMEM;
581 }
582 }
583
584 rcu_read_unlock();
585
586 if (!rc && out)
587 sidtab_sid2str_put(s, entry, *out, *out_len);
588 return rc;
589}
590
591#endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */