summaryrefslogtreecommitdiffstats
path: root/security/selinux/ss/sidtab.h
blob: 4eff0e49dcb22e490e2a880fd72099b4274e6ec5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * A security identifier table (sidtab) is a lookup table
 * of security context structures indexed by SID value.
 *
 * Original author: Stephen Smalley, <sds@tycho.nsa.gov>
 * Author: Ondrej Mosnacek, <omosnacek@gmail.com>
 *
 * Copyright (C) 2018 Red Hat, Inc.
 */
#ifndef _SS_SIDTAB_H_
#define _SS_SIDTAB_H_

#include <linux/spinlock_types.h>
#include <linux/log2.h>
#include <linux/hashtable.h>

#include "context.h"

struct sidtab_entry {
	u32 sid;
	u32 hash;
	struct context context;
#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
	struct sidtab_str_cache __rcu *cache;
#endif
	struct hlist_node list;
};

union sidtab_entry_inner {
	struct sidtab_node_inner *ptr_inner;
	struct sidtab_node_leaf  *ptr_leaf;
};

/* align node size to page boundary */
#define SIDTAB_NODE_ALLOC_SHIFT PAGE_SHIFT
#define SIDTAB_NODE_ALLOC_SIZE  PAGE_SIZE

#define size_to_shift(size) ((size) == 1 ? 1 : (const_ilog2((size) - 1) + 1))

#define SIDTAB_INNER_SHIFT \
	(SIDTAB_NODE_ALLOC_SHIFT - size_to_shift(sizeof(union sidtab_entry_inner)))
#define SIDTAB_INNER_ENTRIES ((size_t)1 << SIDTAB_INNER_SHIFT)
#define SIDTAB_LEAF_ENTRIES \
	(SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry))

#define SIDTAB_MAX_BITS 32
#define SIDTAB_MAX U32_MAX
/* ensure enough tree levels for SIDTAB_MAX entries */
#define SIDTAB_MAX_LEVEL \
	DIV_ROUND_UP(SIDTAB_MAX_BITS - size_to_shift(SIDTAB_LEAF_ENTRIES), \
		     SIDTAB_INNER_SHIFT)

struct sidtab_node_leaf {
	struct sidtab_entry entries[SIDTAB_LEAF_ENTRIES];
};

struct sidtab_node_inner {
	union sidtab_entry_inner entries[SIDTAB_INNER_ENTRIES];
};

struct sidtab_isid_entry {
	int set;
	struct sidtab_entry entry;
};

struct sidtab_convert_params {
	int (*func)(struct context *oldc, struct context *newc, void *args);
	void *args;
	struct sidtab *target;
};

#define SIDTAB_HASH_BITS CONFIG_SECURITY_SELINUX_SIDTAB_HASH_BITS
#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS)

struct sidtab {
	/*
	 * lock-free read access only for as many items as a prior read of
	 * 'count'
	 */
	union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1];
	/*
	 * access atomically via {READ|WRITE}_ONCE(); only increment under
	 * spinlock
	 */
	u32 count;
	/* access only under spinlock */
	struct sidtab_convert_params *convert;
	bool frozen;
	spinlock_t lock;

#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
	/* SID -> context string cache */
	u32 cache_free_slots;
	struct list_head cache_lru_list;
	spinlock_t cache_lock;
#endif

	/* index == SID - 1 (no entry for SECSID_NULL) */
	struct sidtab_isid_entry isids[SECINITSID_NUM];

	/* Hash table for fast reverse context-to-sid lookups. */
	DECLARE_HASHTABLE(context_to_sid, SIDTAB_HASH_BITS);
};

int sidtab_init(struct sidtab *s);
int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context);
struct sidtab_entry *sidtab_search_entry(struct sidtab *s, u32 sid);
struct sidtab_entry *sidtab_search_entry_force(struct sidtab *s, u32 sid);

static inline struct context *sidtab_search(struct sidtab *s, u32 sid)
{
	struct sidtab_entry *entry = sidtab_search_entry(s, sid);

	return entry ? &entry->context : NULL;
}

static inline struct context *sidtab_search_force(struct sidtab *s, u32 sid)
{
	struct sidtab_entry *entry = sidtab_search_entry_force(s, sid);

	return entry ? &entry->context : NULL;
}

int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);

void sidtab_cancel_convert(struct sidtab *s);

void sidtab_freeze_begin(struct sidtab *s, unsigned long *flags) __acquires(&s->lock);
void sidtab_freeze_end(struct sidtab *s, unsigned long *flags) __releases(&s->lock);

int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid);

void sidtab_destroy(struct sidtab *s);

int sidtab_hash_stats(struct sidtab *sidtab, char *page);

#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
void sidtab_sid2str_put(struct sidtab *s, struct sidtab_entry *entry,
			const char *str, u32 str_len);
int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry,
		       char **out, u32 *out_len);
#else
static inline void sidtab_sid2str_put(struct sidtab *s,
				      struct sidtab_entry *entry,
				      const char *str, u32 str_len)
{
}
static inline int sidtab_sid2str_get(struct sidtab *s,
				     struct sidtab_entry *entry,
				     char **out, u32 *out_len)
{
	return -ENOENT;
}
#endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */

#endif	/* _SS_SIDTAB_H_ */