1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <fcntl.h>
17 #include <fstream>
18 #include <string>
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 #include <vector>
24 #include "src/callbacks.h"
25 #include "policycoreutils.h"
26 #include "selinux/selinux.h"
27 #include "selinux_error.h"
28 #include "selinux_klog.h"
29
30 namespace {
31 constexpr int32_t PIPE_NUM = 2;
32 constexpr int32_t BUFF_SIZE = 1024;
33 constexpr const char UPDATER_EXE[] = "/bin/updater";
34 constexpr const char SYSTEM_CIL[] = "/system/etc/selinux/system.cil";
35 constexpr const char SYSTEM_DEVELOPER_CIL[] = "/system/etc/selinux/system_developer.cil";
36 constexpr const char SYSTEM_COMMON_CIL[] = "/system/etc/selinux/system_common.cil";
37 constexpr const char VENDOR_CIL[] = "/vendor/etc/selinux/vendor.cil";
38 constexpr const char VENDOR_DEVELOPER_CIL[] = "/vendor/etc/selinux/vendor_developer.cil";
39 constexpr const char VENDOR_COMMON_CIL[] = "/vendor/etc/selinux/vendor_common.cil";
40 constexpr const char PUBLIC_CIL[] = "/vendor/etc/selinux/public.cil";
41 constexpr const char PUBLIC_DEVELOPER_CIL[] = "/vendor/etc/selinux/public_developer.cil";
42 constexpr const char PUBLIC_COMMON_CIL[] = "/vendor/etc/selinux/public_common.cil";
43 constexpr const char SYSTEM_CIL_HASH[] = "/system/etc/selinux/system.cil.sha256";
44 constexpr const char DEVELOPER_SYSTEM_CIL_HASH[] = "/system/etc/selinux/system_developer.cil.sha256";
45 constexpr const char PRECOMPILED_POLICY_SYSTEM_CIL_HASH[] = "/vendor/etc/selinux/prebuild_sepolicy.system.cil.sha256";
46 constexpr const char PRECOMPILED_DEVELOPER_POLICY_SYSTEM_CIL_HASH[] =
47 "/vendor/etc/selinux/prebuild_sepolicy.system_developer.cil.sha256";
48 constexpr const char COMPILE_OUTPUT_POLICY[] = "/dev/policy.31";
49 constexpr const char DEFAULT_POLICY[] = "/system/etc/selinux/targeted/policy/policy.31";
50 constexpr const char DEFAULT_DEVELOPER_POLICY[] = "/system/etc/selinux/targeted/policy/developer_policy";
51 constexpr const char PRECOMPILED_POLICY[] = "/vendor/etc/selinux/prebuild_sepolicy/policy.31";
52 constexpr const char PRECOMPILED_DEVELOPER_POLICY[] = "/vendor/etc/selinux/prebuild_sepolicy/developer_policy";
53 constexpr const char VERSION_POLICY_PATH[] = "/vendor/etc/selinux/version";
54 constexpr const char COMPATIBLE_CIL_PATH[] = "/system/etc/selinux/compatible/";
55 constexpr const char COMPATIBLE_DEVELOPER_CIL_PATH[] = "/system/etc/selinux/compatible_developer/";
56 #ifdef WITH_DEVELOPER
57 constexpr const char PROC_DSMM_DEVELOPER[] = "/proc/dsmm/developer";
58 #endif
59 } // namespace
60
InitSelinuxLog(void)61 static void InitSelinuxLog(void)
62 {
63 // set selinux log callback
64 SetSelinuxKmsgLevel(SELINUX_KWARN);
65 union selinux_callback cb;
66 cb.func_log = SelinuxKmsg;
67 selinux_set_callback(SELINUX_CB_LOG, cb);
68 }
69
ReadFileFirstLine(const std::string & file,std::string & line)70 static bool ReadFileFirstLine(const std::string &file, std::string &line)
71 {
72 line.clear();
73 if (access(file.c_str(), R_OK) != 0) {
74 selinux_log(SELINUX_ERROR, "Access file %s failed\n", file.c_str());
75 return false;
76 }
77 std::ifstream hashFile(file);
78 if (!hashFile) {
79 selinux_log(SELINUX_ERROR, "Open file %s failed\n", file.c_str());
80 return false;
81 }
82 std::getline(hashFile, line);
83 hashFile.close();
84 return true;
85 }
86
CompareHash(const std::string & file1,const std::string & file2)87 static bool CompareHash(const std::string &file1, const std::string &file2)
88 {
89 std::string line1;
90 std::string line2;
91 if (!ReadFileFirstLine(file1, line1) || !ReadFileFirstLine(file2, line2)) {
92 return false;
93 }
94 return (!line1.empty()) && (!line2.empty()) && (line1 == line2);
95 }
96
DeleteTmpPolicyFile(const std::string & policyFile)97 static void DeleteTmpPolicyFile(const std::string &policyFile)
98 {
99 if ((policyFile == COMPILE_OUTPUT_POLICY) && (access(policyFile.c_str(), R_OK) == 0)) {
100 unlink(policyFile.c_str());
101 }
102 }
103
GetVendorPolicyVersion(std::string & version)104 static bool GetVendorPolicyVersion(std::string &version)
105 {
106 if (!ReadFileFirstLine(VERSION_POLICY_PATH, version)) {
107 return false;
108 }
109 return !version.empty();
110 }
111
ReadPolicyFile(const std::string & policyFile,void ** data,size_t & size)112 static bool ReadPolicyFile(const std::string &policyFile, void **data, size_t &size)
113 {
114 int fd = open(policyFile.c_str(), O_RDONLY | O_CLOEXEC);
115 if (fd < 0) {
116 selinux_log(SELINUX_ERROR, "Open policy file failed\n");
117 DeleteTmpPolicyFile(policyFile);
118 return false;
119 }
120 struct stat sb;
121 if (fstat(fd, &sb) < 0) {
122 selinux_log(SELINUX_ERROR, "Stat policy file failed\n");
123 close(fd);
124 DeleteTmpPolicyFile(policyFile);
125 return false;
126 }
127 if (sb.st_size < 0) {
128 close(fd);
129 DeleteTmpPolicyFile(policyFile);
130 return false;
131 }
132 size = static_cast<size_t>(sb.st_size);
133 *data = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
134 if (*data == MAP_FAILED) {
135 selinux_log(SELINUX_ERROR, "Mmap policy file failed\n");
136 close(fd);
137 DeleteTmpPolicyFile(policyFile);
138 return false;
139 }
140 close(fd);
141 DeleteTmpPolicyFile(policyFile);
142 return true;
143 }
144
GetSelinuxConfigFromFile(int & config)145 static bool GetSelinuxConfigFromFile(int &config)
146 {
147 // get config from /system/etc/selinux/config
148 return (selinux_getenforcemode(&config) == 0) && (config >= 0);
149 }
150
GetSelinuxConfigFromCmdLine(int & config)151 static bool GetSelinuxConfigFromCmdLine(int &config)
152 {
153 // get config from /proc/cmdline
154 std::string cmdFile = "/proc/cmdline";
155 std::string line;
156 if (!ReadFileFirstLine(cmdFile, line)) {
157 return false;
158 }
159
160 std::string key = " enforcing=";
161 size_t index = line.find(key);
162 if (index != line.npos) {
163 int value = line[index + key.size()] - '0';
164 selinux_log(SELINUX_INFO, "Read cmdline enforcing=%d\n", value);
165 if ((value == 0) || (value == 1)) {
166 config = value;
167 return true;
168 }
169 }
170 return false;
171 }
172
SetEnforceState(int newEnforceState)173 static bool SetEnforceState(int newEnforceState)
174 {
175 int oldEnforceState = security_getenforce(); // get from /sys/fs/selinux/enforce
176 if (oldEnforceState < 0) {
177 selinux_log(SELINUX_ERROR, "Security getenforce failed\n");
178 return false;
179 }
180 if (oldEnforceState != newEnforceState) {
181 if (security_setenforce(newEnforceState) < 0) {
182 selinux_log(SELINUX_ERROR, "Security setenforce failed\n");
183 return false;
184 }
185 }
186 return true;
187 }
188
GetEnforceConfig(void)189 static int GetEnforceConfig(void)
190 {
191 int cmdConfig;
192 int fileConfig;
193 int enforce;
194 if (GetSelinuxConfigFromCmdLine(cmdConfig)) {
195 enforce = cmdConfig;
196 } else if (GetSelinuxConfigFromFile(fileConfig)) {
197 enforce = fileConfig;
198 } else {
199 enforce = 0;
200 }
201 selinux_log(SELINUX_INFO, "Get enforce config %d\n", enforce);
202 return enforce;
203 }
204
LoadPolicy(void * data,size_t size)205 static bool LoadPolicy(void *data, size_t size)
206 {
207 set_selinuxmnt("/sys/fs/selinux");
208
209 if (!SetEnforceState(GetEnforceConfig())) {
210 return false;
211 }
212
213 if (security_load_policy(data, size) < 0) {
214 selinux_log(SELINUX_ERROR, "Security load policy failed\n");
215 return false;
216 }
217 return true;
218 }
219
GetVersionPolicy(std::string & versionPolicy,bool devMode)220 static bool GetVersionPolicy(std::string &versionPolicy, bool devMode)
221 {
222 std::string version;
223 if (!GetVendorPolicyVersion(version)) {
224 selinux_log(SELINUX_ERROR, "Get vendor policy version failed\n");
225 return false;
226 }
227 std::string path((devMode ? COMPATIBLE_DEVELOPER_CIL_PATH : COMPATIBLE_CIL_PATH) + version + ".cil");
228 if (access(path.c_str(), F_OK) == 0) {
229 versionPolicy = path;
230 return true;
231 }
232 selinux_log(SELINUX_ERROR, "Get vendor version policy failed\n");
233 return false;
234 }
235
WaitForChild(pid_t pid)236 static bool WaitForChild(pid_t pid)
237 {
238 int status = -1;
239 if (waitpid(pid, &status, 0) < 0) {
240 selinux_log(SELINUX_ERROR, "Waitpid failed\n");
241 return false;
242 }
243 if (WIFEXITED(status)) {
244 int exitCode = WEXITSTATUS(status);
245 selinux_log(SELINUX_INFO, "Child terminated by exit %d\n", exitCode);
246 if (exitCode == 0) {
247 return true;
248 }
249 } else if (WIFSIGNALED(status)) {
250 selinux_log(SELINUX_ERROR, "Child terminated by signal %d\n", WTERMSIG(status));
251 } else if (WIFSTOPPED(status)) {
252 selinux_log(SELINUX_ERROR, "Child stopped by signal %d\n", WSTOPSIG(status));
253 } else {
254 selinux_log(SELINUX_ERROR, "Child exit with status %d\n", status);
255 }
256 return false;
257 }
258
CompilePolicyWithFork(std::vector<const char * > & compileCmd)259 static bool CompilePolicyWithFork(std::vector<const char *> &compileCmd)
260 {
261 int pipeFd[PIPE_NUM];
262 if (pipe(pipeFd) < 0) {
263 selinux_log(SELINUX_ERROR, "Create pipe failed, %d, %s\n", errno, strerror(errno));
264 return false;
265 }
266 pid_t pid = fork();
267 if (pid < 0) {
268 selinux_log(SELINUX_ERROR, "Fork subprocess failed, %d, %s\n", errno, strerror(errno));
269 (void)close(pipeFd[0]);
270 (void)close(pipeFd[1]);
271 return false;
272 }
273 if (pid == 0) {
274 (void)close(pipeFd[0]);
275 if (dup2(pipeFd[1], STDERR_FILENO) == -1) {
276 selinux_log(SELINUX_ERROR, "Dup2 failed, %d, %s\n", errno, strerror(errno));
277 (void)close(pipeFd[1]);
278 _exit(1);
279 }
280 (void)close(pipeFd[1]);
281 if (execv(compileCmd[0], const_cast<char **>(compileCmd.data())) == -1) {
282 selinux_log(SELINUX_ERROR, "Execv subprocess failed, %d, %s\n", errno, strerror(errno));
283 return false;
284 }
285 _exit(1);
286 }
287 (void)close(pipeFd[1]);
288
289 FILE *fp = fdopen(pipeFd[0], "r");
290 if (fp != nullptr) {
291 char buf[BUFF_SIZE] = {0};
292 while (fgets(buf, sizeof(buf) - 1, fp) != nullptr) {
293 size_t n = strlen(buf);
294 if (n == 0) {
295 continue;
296 }
297 if (buf[n - 1] == '\n') {
298 buf[n - 1] = '\0';
299 }
300 if (strstr(buf, "Failed") != nullptr) {
301 selinux_log(SELINUX_ERROR, "SELinux compile result: %s\n", buf);
302 }
303 }
304 fclose(fp);
305 } else {
306 selinux_log(SELINUX_ERROR, "Fopen pipe failed, %d, %s\n", errno, strerror(errno));
307 (void)close(pipeFd[0]);
308 }
309
310 return WaitForChild(pid);
311 }
312
AddPolicy(std::vector<const char * > & compileCmd,const char * policyPath)313 static void AddPolicy(std::vector<const char *> &compileCmd, const char *policyPath)
314 {
315 if (access(policyPath, F_OK) == 0) {
316 selinux_log(SELINUX_WARNING, "Add policy %s\n", policyPath);
317 compileCmd.emplace_back(policyPath);
318 return;
319 }
320 }
321
CompilePolicy(bool devMode)322 static bool CompilePolicy(bool devMode)
323 {
324 std::vector<const char *> compileCmd = {
325 "/system/bin/secilc",
326 "-m",
327 "-N",
328 "-M",
329 "true",
330 "-G",
331 "-c",
332 "31",
333 "-O",
334 "-f",
335 "/sys/fs/selinux/null",
336 "-o",
337 COMPILE_OUTPUT_POLICY,
338 };
339 AddPolicy(compileCmd, SYSTEM_COMMON_CIL);
340 AddPolicy(compileCmd, VENDOR_COMMON_CIL);
341 AddPolicy(compileCmd, PUBLIC_COMMON_CIL);
342 AddPolicy(compileCmd, devMode ? SYSTEM_DEVELOPER_CIL : SYSTEM_CIL);
343 AddPolicy(compileCmd, devMode ? VENDOR_DEVELOPER_CIL : VENDOR_CIL);
344 AddPolicy(compileCmd, devMode ? PUBLIC_DEVELOPER_CIL : PUBLIC_CIL);
345
346 std::string versionPolicy;
347 if (GetVersionPolicy(versionPolicy, devMode)) {
348 AddPolicy(compileCmd, versionPolicy.c_str());
349 }
350
351 compileCmd.emplace_back(nullptr);
352
353 return CompilePolicyWithFork(compileCmd);
354 }
355
IsDeveloperMode()356 static bool IsDeveloperMode()
357 {
358 #ifdef WITH_DEVELOPER
359 if ((access(SYSTEM_DEVELOPER_CIL, R_OK) != 0) || (access(VENDOR_DEVELOPER_CIL, R_OK) != 0)) {
360 selinux_log(SELINUX_ERROR, "No developer cil file found, fallback to normal mode\n");
361 return false;
362 }
363 std::string devMode;
364 if (!ReadFileFirstLine(PROC_DSMM_DEVELOPER, devMode)) {
365 return false;
366 }
367 selinux_log(SELINUX_WARNING, "Get developer mode, %s\n", devMode.c_str());
368 return devMode == "const.security.developermode.state=true";
369 #else
370 return false;
371 #endif
372 }
373
374 // Check if in updater mode.
IsUpdaterMode(void)375 static bool IsUpdaterMode(void)
376 {
377 return access(UPDATER_EXE, X_OK) == 0;
378 }
379
GetPolicyFile(std::string & policyFile,bool devMode)380 static bool GetPolicyFile(std::string &policyFile, bool devMode)
381 {
382 if (IsUpdaterMode()) {
383 policyFile = DEFAULT_POLICY;
384 selinux_log(SELINUX_WARNING, "Updater mode, load %s\n", policyFile.c_str());
385 return true;
386 }
387
388 if (access(SYSTEM_CIL, R_OK) != 0) { // no system.cil file
389 policyFile = devMode ? DEFAULT_DEVELOPER_POLICY : DEFAULT_POLICY;
390 selinux_log(SELINUX_WARNING, "No cil file found, load %s\n", policyFile.c_str());
391 return true;
392 }
393
394 // check precompiled policy
395 if (CompareHash(devMode ? PRECOMPILED_DEVELOPER_POLICY_SYSTEM_CIL_HASH : PRECOMPILED_POLICY_SYSTEM_CIL_HASH,
396 devMode ? DEVELOPER_SYSTEM_CIL_HASH : SYSTEM_CIL_HASH)) {
397 if (access(devMode ? PRECOMPILED_DEVELOPER_POLICY : PRECOMPILED_POLICY, R_OK) == 0) {
398 policyFile = devMode ? PRECOMPILED_DEVELOPER_POLICY : PRECOMPILED_POLICY;
399 selinux_log(SELINUX_WARNING, "Found precompiled policy, load %s\n", policyFile.c_str());
400 return true;
401 }
402 }
403
404 // no precompiled policy, compile from cil
405 selinux_log(SELINUX_WARNING, "No precompiled policy found, compile it\n");
406 if (CompilePolicy(devMode)) {
407 policyFile = COMPILE_OUTPUT_POLICY;
408 return true;
409 }
410 return false;
411 }
412
LoadPolicyFromFile(const std::string & policyFile)413 static int LoadPolicyFromFile(const std::string &policyFile)
414 {
415 void *data = nullptr;
416 size_t size = 0;
417 if (!ReadPolicyFile(policyFile, &data, size)) {
418 return -1;
419 }
420 if (!LoadPolicy(data, size)) {
421 munmap(data, size);
422 return -1;
423 }
424 munmap(data, size);
425 return 0;
426 }
427
LoadPolicy(void)428 int LoadPolicy(void)
429 {
430 InitSelinuxLog();
431 std::string policyFile;
432 if (!GetPolicyFile(policyFile, IsDeveloperMode())) {
433 return -1;
434 }
435 return LoadPolicyFromFile(policyFile);
436 }
437