162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * A security identifier table (sidtab) is a lookup table
462306a36Sopenharmony_ci * of security context structures indexed by SID value.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Original author: Stephen Smalley, <stephen.smalley.work@gmail.com>
762306a36Sopenharmony_ci * Author: Ondrej Mosnacek, <omosnacek@gmail.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright (C) 2018 Red Hat, Inc.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#ifndef _SS_SIDTAB_H_
1262306a36Sopenharmony_ci#define _SS_SIDTAB_H_
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/spinlock_types.h>
1562306a36Sopenharmony_ci#include <linux/log2.h>
1662306a36Sopenharmony_ci#include <linux/hashtable.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "context.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct sidtab_entry {
2162306a36Sopenharmony_ci	u32 sid;
2262306a36Sopenharmony_ci	u32 hash;
2362306a36Sopenharmony_ci	struct context context;
2462306a36Sopenharmony_ci#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
2562306a36Sopenharmony_ci	struct sidtab_str_cache __rcu *cache;
2662306a36Sopenharmony_ci#endif
2762306a36Sopenharmony_ci	struct hlist_node list;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciunion sidtab_entry_inner {
3162306a36Sopenharmony_ci	struct sidtab_node_inner *ptr_inner;
3262306a36Sopenharmony_ci	struct sidtab_node_leaf  *ptr_leaf;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* align node size to page boundary */
3662306a36Sopenharmony_ci#define SIDTAB_NODE_ALLOC_SHIFT PAGE_SHIFT
3762306a36Sopenharmony_ci#define SIDTAB_NODE_ALLOC_SIZE  PAGE_SIZE
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define size_to_shift(size) ((size) == 1 ? 1 : (const_ilog2((size) - 1) + 1))
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define SIDTAB_INNER_SHIFT \
4262306a36Sopenharmony_ci	(SIDTAB_NODE_ALLOC_SHIFT - size_to_shift(sizeof(union sidtab_entry_inner)))
4362306a36Sopenharmony_ci#define SIDTAB_INNER_ENTRIES ((size_t)1 << SIDTAB_INNER_SHIFT)
4462306a36Sopenharmony_ci#define SIDTAB_LEAF_ENTRIES \
4562306a36Sopenharmony_ci	(SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry))
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define SIDTAB_MAX_BITS 32
4862306a36Sopenharmony_ci#define SIDTAB_MAX U32_MAX
4962306a36Sopenharmony_ci/* ensure enough tree levels for SIDTAB_MAX entries */
5062306a36Sopenharmony_ci#define SIDTAB_MAX_LEVEL \
5162306a36Sopenharmony_ci	DIV_ROUND_UP(SIDTAB_MAX_BITS - size_to_shift(SIDTAB_LEAF_ENTRIES), \
5262306a36Sopenharmony_ci		     SIDTAB_INNER_SHIFT)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct sidtab_node_leaf {
5562306a36Sopenharmony_ci	struct sidtab_entry entries[SIDTAB_LEAF_ENTRIES];
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistruct sidtab_node_inner {
5962306a36Sopenharmony_ci	union sidtab_entry_inner entries[SIDTAB_INNER_ENTRIES];
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct sidtab_isid_entry {
6362306a36Sopenharmony_ci	int set;
6462306a36Sopenharmony_ci	struct sidtab_entry entry;
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistruct sidtab_convert_params {
6862306a36Sopenharmony_ci	struct convert_context_args *args;
6962306a36Sopenharmony_ci	struct sidtab *target;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define SIDTAB_HASH_BITS CONFIG_SECURITY_SELINUX_SIDTAB_HASH_BITS
7362306a36Sopenharmony_ci#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS)
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct sidtab {
7662306a36Sopenharmony_ci	/*
7762306a36Sopenharmony_ci	 * lock-free read access only for as many items as a prior read of
7862306a36Sopenharmony_ci	 * 'count'
7962306a36Sopenharmony_ci	 */
8062306a36Sopenharmony_ci	union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1];
8162306a36Sopenharmony_ci	/*
8262306a36Sopenharmony_ci	 * access atomically via {READ|WRITE}_ONCE(); only increment under
8362306a36Sopenharmony_ci	 * spinlock
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	u32 count;
8662306a36Sopenharmony_ci	/* access only under spinlock */
8762306a36Sopenharmony_ci	struct sidtab_convert_params *convert;
8862306a36Sopenharmony_ci	bool frozen;
8962306a36Sopenharmony_ci	spinlock_t lock;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
9262306a36Sopenharmony_ci	/* SID -> context string cache */
9362306a36Sopenharmony_ci	u32 cache_free_slots;
9462306a36Sopenharmony_ci	struct list_head cache_lru_list;
9562306a36Sopenharmony_ci	spinlock_t cache_lock;
9662306a36Sopenharmony_ci#endif
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* index == SID - 1 (no entry for SECSID_NULL) */
9962306a36Sopenharmony_ci	struct sidtab_isid_entry isids[SECINITSID_NUM];
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* Hash table for fast reverse context-to-sid lookups. */
10262306a36Sopenharmony_ci	DECLARE_HASHTABLE(context_to_sid, SIDTAB_HASH_BITS);
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciint sidtab_init(struct sidtab *s);
10662306a36Sopenharmony_ciint sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context);
10762306a36Sopenharmony_cistruct sidtab_entry *sidtab_search_entry(struct sidtab *s, u32 sid);
10862306a36Sopenharmony_cistruct sidtab_entry *sidtab_search_entry_force(struct sidtab *s, u32 sid);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic inline struct context *sidtab_search(struct sidtab *s, u32 sid)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct sidtab_entry *entry = sidtab_search_entry(s, sid);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return entry ? &entry->context : NULL;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic inline struct context *sidtab_search_force(struct sidtab *s, u32 sid)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct sidtab_entry *entry = sidtab_search_entry_force(s, sid);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return entry ? &entry->context : NULL;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ciint sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_civoid sidtab_cancel_convert(struct sidtab *s);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_civoid sidtab_freeze_begin(struct sidtab *s, unsigned long *flags) __acquires(&s->lock);
12962306a36Sopenharmony_civoid sidtab_freeze_end(struct sidtab *s, unsigned long *flags) __releases(&s->lock);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ciint sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_civoid sidtab_destroy(struct sidtab *s);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciint sidtab_hash_stats(struct sidtab *sidtab, char *page);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
13862306a36Sopenharmony_civoid sidtab_sid2str_put(struct sidtab *s, struct sidtab_entry *entry,
13962306a36Sopenharmony_ci			const char *str, u32 str_len);
14062306a36Sopenharmony_ciint sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry,
14162306a36Sopenharmony_ci		       char **out, u32 *out_len);
14262306a36Sopenharmony_ci#else
14362306a36Sopenharmony_cistatic inline void sidtab_sid2str_put(struct sidtab *s,
14462306a36Sopenharmony_ci				      struct sidtab_entry *entry,
14562306a36Sopenharmony_ci				      const char *str, u32 str_len)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_cistatic inline int sidtab_sid2str_get(struct sidtab *s,
14962306a36Sopenharmony_ci				     struct sidtab_entry *entry,
15062306a36Sopenharmony_ci				     char **out, u32 *out_len)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	return -ENOENT;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci#endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci#endif	/* _SS_SIDTAB_H_ */
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci
159