-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Expand file tree
/
Copy pathselinux.c
More file actions
205 lines (183 loc) · 5.48 KB
/
Copy pathselinux.c
File metadata and controls
205 lines (183 loc) · 5.48 KB
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include "selinux.h"
#include "linux/cred.h"
#include "linux/sched.h"
#include "objsec.h"
#include "linux/version.h"
#include "../klog.h" // IWYU pragma: keep
#include "../ksu.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 18, 0)
#define cred_security_struct task_security_struct
#endif
/*
* Cached SID values for frequently checked contexts.
* These are resolved once at init and used for fast u32 comparison
* instead of expensive string operations on every check.
*
* A value of 0 means "no cached SID is available" for that context.
* This covers both the initial "not yet cached" state and any case
* where resolving the SID (e.g. via security_secctx_to_secid) failed.
* In all such cases we intentionally fall back to the slower
* string-based comparison path; this degrades performance only and
* does not cause a functional failure.
*/
static u32 cached_su_sid __read_mostly = 0;
static u32 cached_zygote_sid __read_mostly = 0;
static u32 cached_init_sid __read_mostly = 0;
u32 ksu_file_sid __read_mostly = 0;
static int transive_to_domain(const char *domain, struct cred *cred)
{
u32 sid;
int error;
struct cred_security_struct *tsec;
tsec = selinux_cred(cred);
if (!tsec) {
pr_err("tsec == NULL!\n");
return -1;
}
error = security_secctx_to_secid(domain, strlen(domain), &sid);
if (error) {
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n", domain,
sid, error);
}
if (!error) {
tsec->sid = sid;
tsec->create_sid = 0;
tsec->keycreate_sid = 0;
tsec->sockcreate_sid = 0;
}
return error;
}
void setup_selinux(const char *domain, struct cred *cred)
{
if (transive_to_domain(domain, cred)) {
pr_err("transive domain failed.\n");
return;
}
}
void setup_ksu_cred(void)
{
if (ksu_cred && transive_to_domain(KERNEL_SU_CONTEXT, ksu_cred)) {
pr_err("setup ksu cred failed.\n");
}
}
void setenforce(bool enforce)
{
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
selinux_state.enforcing = enforce;
#endif
}
bool getenforce(void)
{
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
if (selinux_state.disabled) {
return false;
}
#endif
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
return selinux_state.enforcing;
#else
return true;
#endif
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0)
struct lsm_context {
char *context;
u32 len;
};
static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp)
{
return security_secid_to_secctx(secid, &cp->context, &cp->len);
}
static void __security_release_secctx(struct lsm_context *cp)
{
security_release_secctx(cp->context, cp->len);
}
#else
#define __security_secid_to_secctx security_secid_to_secctx
#define __security_release_secctx security_release_secctx
#endif
/*
* Initialize cached SID values for frequently checked SELinux contexts.
* Called once after SELinux policy is loaded (post-fs-data).
* This eliminates expensive string comparisons in hot paths.
*/
void cache_sid(void)
{
int err;
err = security_secctx_to_secid(KERNEL_SU_CONTEXT, strlen(KERNEL_SU_CONTEXT),
&cached_su_sid);
if (err) {
pr_warn("Failed to cache kernel su domain SID: %d\n", err);
cached_su_sid = 0;
} else {
pr_info("Cached su SID: %u\n", cached_su_sid);
}
err = security_secctx_to_secid(ZYGOTE_CONTEXT, strlen(ZYGOTE_CONTEXT),
&cached_zygote_sid);
if (err) {
pr_warn("Failed to cache zygote SID: %d\n", err);
cached_zygote_sid = 0;
} else {
pr_info("Cached zygote SID: %u\n", cached_zygote_sid);
}
err = security_secctx_to_secid(INIT_CONTEXT, strlen(INIT_CONTEXT),
&cached_init_sid);
if (err) {
pr_warn("Failed to cache init SID: %d\n", err);
cached_init_sid = 0;
} else {
pr_info("Cached init SID: %u\n", cached_init_sid);
}
err = security_secctx_to_secid(KSU_FILE_CONTEXT, strlen(KSU_FILE_CONTEXT),
&ksu_file_sid);
if (err) {
pr_warn("Failed to cache ksu_file SID: %d\n", err);
ksu_file_sid = 0;
} else {
pr_info("Cached ksu_file SID: %u\n", ksu_file_sid);
}
}
/*
* Fast path: compare task's SID directly against cached value.
* Falls back to string comparison if cache is not initialized.
*/
static bool is_sid_match(const struct cred *cred, u32 cached_sid,
const char *fallback_context)
{
if (!cred) {
return false;
}
const struct cred_security_struct *tsec = selinux_cred(cred);
if (!tsec) {
return false;
}
// Fast path: use cached SID if available
if (likely(cached_sid != 0)) {
return tsec->sid == cached_sid;
}
// Slow path fallback: string comparison (only before cache is initialized)
struct lsm_context ctx;
bool result;
if (__security_secid_to_secctx(tsec->sid, &ctx)) {
return false;
}
result = strncmp(fallback_context, ctx.context, ctx.len) == 0;
__security_release_secctx(&ctx);
return result;
}
bool is_task_ksu_domain(const struct cred *cred)
{
return is_sid_match(cred, cached_su_sid, KERNEL_SU_CONTEXT);
}
bool is_ksu_domain(void)
{
return is_task_ksu_domain(current_cred());
}
bool is_zygote(const struct cred *cred)
{
return is_sid_match(cred, cached_zygote_sid, ZYGOTE_CONTEXT);
}
bool is_init(const struct cred *cred)
{
return is_sid_match(cred, cached_init_sid, INIT_CONTEXT);
}