• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 BACKUP_PRECOMPILED_POLICY[] = "/system/etc/selinux/policy.31";
54 constexpr const char BACKUP_PRECOMPILED_DEVELOPER_POLICY[] = "/system/etc/selinux/developer_policy";
55 constexpr const char VERSION_POLICY_PATH[] = "/vendor/etc/selinux/version";
56 constexpr const char COMPATIBLE_CIL_PATH[] = "/system/etc/selinux/compatible/";
57 constexpr const char COMPATIBLE_DEVELOPER_CIL_PATH[] = "/system/etc/selinux/compatible_developer/";
58 #ifdef WITH_DEVELOPER
59 constexpr const char PROC_DSMM_DEVELOPER[] = "/proc/dsmm/developer";
60 #endif
61 } // namespace
62 
InitSelinuxLog(void)63 static void InitSelinuxLog(void)
64 {
65     // set selinux log callback
66     SetSelinuxKmsgLevel(SELINUX_KWARN);
67     union selinux_callback cb;
68     cb.func_log = SelinuxKmsg;
69     selinux_set_callback(SELINUX_CB_LOG, cb);
70 }
71 
ReadFileFirstLine(const std::string & file,std::string & line)72 static bool ReadFileFirstLine(const std::string &file, std::string &line)
73 {
74     line.clear();
75     if (access(file.c_str(), R_OK) != 0) {
76         selinux_log(SELINUX_ERROR, "Access file %s failed\n", file.c_str());
77         return false;
78     }
79     std::ifstream hashFile(file);
80     if (!hashFile) {
81         selinux_log(SELINUX_ERROR, "Open file %s failed\n", file.c_str());
82         return false;
83     }
84     std::getline(hashFile, line);
85     hashFile.close();
86     return true;
87 }
88 
CompareHash(const std::string & file1,const std::string & file2)89 static bool CompareHash(const std::string &file1, const std::string &file2)
90 {
91     std::string line1;
92     std::string line2;
93     if (!ReadFileFirstLine(file1, line1) || !ReadFileFirstLine(file2, line2)) {
94         return false;
95     }
96     return (!line1.empty()) && (!line2.empty()) && (line1 == line2);
97 }
98 
DeleteTmpPolicyFile(const std::string & policyFile)99 static void DeleteTmpPolicyFile(const std::string &policyFile)
100 {
101     if ((policyFile == COMPILE_OUTPUT_POLICY) && (access(policyFile.c_str(), R_OK) == 0)) {
102         unlink(policyFile.c_str());
103     }
104 }
105 
GetVendorPolicyVersion(std::string & version)106 static bool GetVendorPolicyVersion(std::string &version)
107 {
108     if (!ReadFileFirstLine(VERSION_POLICY_PATH, version)) {
109         return false;
110     }
111     return !version.empty();
112 }
113 
ReadPolicyFile(const std::string & policyFile,void ** data,size_t & size)114 static bool ReadPolicyFile(const std::string &policyFile, void **data, size_t &size)
115 {
116     int fd = open(policyFile.c_str(), O_RDONLY | O_CLOEXEC);
117     if (fd < 0) {
118         selinux_log(SELINUX_ERROR, "Open policy file failed\n");
119         DeleteTmpPolicyFile(policyFile);
120         return false;
121     }
122     struct stat sb;
123     if (fstat(fd, &sb) < 0) {
124         selinux_log(SELINUX_ERROR, "Stat policy file failed\n");
125         close(fd);
126         DeleteTmpPolicyFile(policyFile);
127         return false;
128     }
129     if (sb.st_size < 0) {
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         return false;
287     }
288     (void)close(pipeFd[1]);
289 
290     FILE *fp = fdopen(pipeFd[0], "r");
291     if (fp != nullptr) {
292         char buf[BUFF_SIZE] = {0};
293         while (fgets(buf, sizeof(buf) - 1, fp) != nullptr) {
294             size_t n = strlen(buf);
295             if (n == 0) {
296                 continue;
297             }
298             if (buf[n - 1] == '\n') {
299                 buf[n - 1] = '\0';
300             }
301             if (strstr(buf, "Failed") != nullptr) {
302                 selinux_log(SELINUX_ERROR, "SELinux compile result: %s\n", buf);
303             }
304         }
305         fclose(fp);
306     }
307 
308     (void)close(pipeFd[0]);
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     std::string devMode;
360     if (!ReadFileFirstLine(PROC_DSMM_DEVELOPER, devMode)) {
361         return false;
362     }
363     selinux_log(SELINUX_WARNING, "Get developer mode, %s\n", devMode.c_str());
364     return devMode == "const.security.developermode.state=true";
365 #else
366     return false;
367 #endif
368 }
369 
370 // Check if in updater mode.
IsUpdaterMode(void)371 static bool IsUpdaterMode(void)
372 {
373     return access(UPDATER_EXE, X_OK) == 0;
374 }
375 
GetPolicyFile(std::string & policyFile,bool devMode)376 static bool GetPolicyFile(std::string &policyFile, bool devMode)
377 {
378     if (IsUpdaterMode()) {
379         policyFile = DEFAULT_POLICY;
380         selinux_log(SELINUX_WARNING, "Updater mode, load %s\n", policyFile.c_str());
381         return true;
382     }
383 
384     if (access(SYSTEM_CIL, R_OK) != 0) { // no system.cil file
385         policyFile = devMode ? DEFAULT_DEVELOPER_POLICY : DEFAULT_POLICY;
386         selinux_log(SELINUX_WARNING, "No cil file found, load %s\n", policyFile.c_str());
387         return true;
388     }
389 
390     // check precompiled policy
391     if (CompareHash(devMode ? PRECOMPILED_DEVELOPER_POLICY_SYSTEM_CIL_HASH : PRECOMPILED_POLICY_SYSTEM_CIL_HASH,
392                     devMode ? DEVELOPER_SYSTEM_CIL_HASH : SYSTEM_CIL_HASH)) {
393         if (access(devMode ? BACKUP_PRECOMPILED_DEVELOPER_POLICY : BACKUP_PRECOMPILED_POLICY, R_OK) == 0) {
394             policyFile = devMode ? BACKUP_PRECOMPILED_DEVELOPER_POLICY : BACKUP_PRECOMPILED_POLICY;
395             selinux_log(SELINUX_WARNING, "Found precompiled policy, load %s\n", policyFile.c_str());
396             return true;
397         }
398         if (access(devMode ? PRECOMPILED_DEVELOPER_POLICY : PRECOMPILED_POLICY, R_OK) == 0) {
399             policyFile = devMode ? PRECOMPILED_DEVELOPER_POLICY : PRECOMPILED_POLICY;
400             selinux_log(SELINUX_WARNING, "Found precompiled policy, load %s\n", policyFile.c_str());
401             return true;
402         }
403     }
404 
405     // no precompiled policy, compile from cil
406     selinux_log(SELINUX_WARNING, "No precompiled policy found, compile it\n");
407     if (CompilePolicy(devMode)) {
408         policyFile = COMPILE_OUTPUT_POLICY;
409         return true;
410     }
411     return false;
412 }
413 
LoadPolicyFromFile(const std::string & policyFile)414 static int LoadPolicyFromFile(const std::string &policyFile)
415 {
416     void *data = nullptr;
417     size_t size = 0;
418     if (!ReadPolicyFile(policyFile, &data, size)) {
419         return -1;
420     }
421     if (!LoadPolicy(data, size)) {
422         munmap(data, size);
423         return -1;
424     }
425     munmap(data, size);
426     return 0;
427 }
428 
LoadPolicy(void)429 int LoadPolicy(void)
430 {
431     InitSelinuxLog();
432     std::string policyFile;
433     if (!GetPolicyFile(policyFile, IsDeveloperMode())) {
434         return -1;
435     }
436     return LoadPolicyFromFile(policyFile);
437 }
438