• 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 SYSTEM_CIL[] = "/system/etc/selinux/system.cil";
34 constexpr const char VENDOR_CIL[] = "/vendor/etc/selinux/vendor.cil";
35 constexpr const char PUBLIC_CIL[] = "/vendor/etc/selinux/public.cil";
36 constexpr const char SYSTEM_CIL_HASH[] = "/system/etc/selinux/system.cil.sha256";
37 constexpr const char PRECOMPILED_POLICY_SYSTEM_CIL_HASH[] = "/vendor/etc/selinux/prebuild_sepolicy.system.cil.sha256";
38 constexpr const char COMPILE_OUTPUT_POLICY[] = "/dev/policy.31";
39 constexpr const char DEFAULT_POLICY[] = "/system/etc/selinux/targeted/policy/policy.31";
40 constexpr const char PRECOMPILED_POLICY[] = "/vendor/etc/selinux/prebuild_sepolicy/policy.31";
41 constexpr const char BACKUP_PRECOMPILED_POLICY[] = "/system/etc/selinux/policy.31";
42 constexpr const char VERSION_POLICY_PATH[] = "/vendor/etc/selinux/version";
43 constexpr const char COMPATIBLE_CIL_PATH[] = "/system/etc/selinux/compatible/";
44 } // namespace
45 
InitSelinuxLog(void)46 static void InitSelinuxLog(void)
47 {
48     // set selinux log callback
49     SetSelinuxKmsgLevel(SELINUX_KWARN);
50     union selinux_callback cb;
51     cb.func_log = SelinuxKmsg;
52     selinux_set_callback(SELINUX_CB_LOG, cb);
53 }
54 
ReadFileFirstLine(const std::string & file,std::string & line)55 static bool ReadFileFirstLine(const std::string &file, std::string &line)
56 {
57     line.clear();
58     if (access(file.c_str(), R_OK) != 0) {
59         selinux_log(SELINUX_ERROR, "Access file %s failed\n", file.c_str());
60         return false;
61     }
62     std::ifstream hashFile(file);
63     if (!hashFile) {
64         selinux_log(SELINUX_ERROR, "Open file %s failed\n", file.c_str());
65         return false;
66     }
67     std::getline(hashFile, line);
68     hashFile.close();
69     return true;
70 }
71 
CompareHash(const std::string & file1,const std::string & file2)72 static bool CompareHash(const std::string &file1, const std::string &file2)
73 {
74     std::string line1;
75     std::string line2;
76     if (!ReadFileFirstLine(file1, line1) || !ReadFileFirstLine(file2, line2)) {
77         return false;
78     }
79     return (!line1.empty()) && (!line2.empty()) && (line1 == line2);
80 }
81 
DeleteTmpPolicyFile(const std::string & policyFile)82 static void DeleteTmpPolicyFile(const std::string &policyFile)
83 {
84     if ((policyFile == COMPILE_OUTPUT_POLICY) && (access(policyFile.c_str(), R_OK) == 0)) {
85         unlink(policyFile.c_str());
86     }
87 }
88 
GetVendorPolicyVersion(std::string & version)89 static bool GetVendorPolicyVersion(std::string & version)
90 {
91     if (!ReadFileFirstLine(VERSION_POLICY_PATH, version)) {
92         return false;
93     }
94     return !version.empty();
95 }
96 
ReadPolicyFile(const std::string & policyFile,void ** data,size_t & size)97 static bool ReadPolicyFile(const std::string &policyFile, void **data, size_t &size)
98 {
99     int fd = open(policyFile.c_str(), O_RDONLY | O_CLOEXEC);
100     if (fd < 0) {
101         selinux_log(SELINUX_ERROR, "Open policy file failed\n");
102         DeleteTmpPolicyFile(policyFile);
103         return false;
104     }
105     struct stat sb;
106     if (fstat(fd, &sb) < 0) {
107         selinux_log(SELINUX_ERROR, "Stat policy file failed\n");
108         close(fd);
109         DeleteTmpPolicyFile(policyFile);
110         return false;
111     }
112     if (sb.st_size < 0) {
113         return false;
114     }
115     size = static_cast<size_t>(sb.st_size);
116     *data = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
117     if (*data == MAP_FAILED) {
118         selinux_log(SELINUX_ERROR, "Mmap policy file failed\n");
119         close(fd);
120         DeleteTmpPolicyFile(policyFile);
121         return false;
122     }
123     close(fd);
124     DeleteTmpPolicyFile(policyFile);
125     return true;
126 }
127 
GetSelinuxConfigFromFile(int & config)128 static bool GetSelinuxConfigFromFile(int &config)
129 {
130     // get config from /system/etc/selinux/config
131     return (selinux_getenforcemode(&config) == 0) && (config >= 0);
132 }
133 
GetSelinuxConfigFromCmdLine(int & config)134 static bool GetSelinuxConfigFromCmdLine(int &config)
135 {
136     // get config from /proc/cmdline
137     std::string cmdFile = "/proc/cmdline";
138     std::string line;
139     if (!ReadFileFirstLine(cmdFile, line)) {
140         return false;
141     }
142 
143     std::string key = " enforcing=";
144     size_t index = line.find(key);
145     if (index != line.npos) {
146         int value = line[index + key.size()] - '0';
147         selinux_log(SELINUX_INFO, "Read cmdline enforcing=%d\n", value);
148         if ((value == 0) || (value == 1)) {
149             config = value;
150             return true;
151         }
152     }
153     return false;
154 }
155 
SetEnforceState(int newEnforceState)156 static bool SetEnforceState(int newEnforceState)
157 {
158     int oldEnforceState = security_getenforce(); // get from /sys/fs/selinux/enforce
159     if (oldEnforceState < 0) {
160         selinux_log(SELINUX_ERROR, "Security getenforce failed\n");
161         return false;
162     }
163     if (oldEnforceState != newEnforceState) {
164         if (security_setenforce(newEnforceState) < 0) {
165             selinux_log(SELINUX_ERROR, "Security setenforce failed\n");
166             return false;
167         }
168     }
169     return true;
170 }
171 
GetEnforceConfig(void)172 static int GetEnforceConfig(void)
173 {
174     int cmdConfig;
175     int fileConfig;
176     int enforce;
177     if (GetSelinuxConfigFromCmdLine(cmdConfig)) {
178         enforce = cmdConfig;
179     } else if (GetSelinuxConfigFromFile(fileConfig)) {
180         enforce = fileConfig;
181     } else {
182         enforce = 0;
183     }
184     selinux_log(SELINUX_INFO, "Get enforce config %d\n", enforce);
185     return enforce;
186 }
187 
LoadPolicy(void * data,size_t size)188 static bool LoadPolicy(void *data, size_t size)
189 {
190     set_selinuxmnt("/sys/fs/selinux");
191 
192     if (!SetEnforceState(GetEnforceConfig())) {
193         return false;
194     }
195 
196     if (security_load_policy(data, size) < 0) {
197         selinux_log(SELINUX_ERROR, "Security load policy failed\n");
198         return false;
199     }
200     return true;
201 }
202 
GetVersionPolicy(std::string & versionPolicy)203 static bool GetVersionPolicy(std::string &versionPolicy)
204 {
205     std::string version;
206     if (!GetVendorPolicyVersion(version)) {
207         selinux_log(SELINUX_ERROR, "Get vendor policy version failed\n");
208         return false;
209     }
210     std::string path(COMPATIBLE_CIL_PATH + version + ".cil");
211     if (access(path.c_str(), F_OK) == 0) {
212         versionPolicy = path;
213         return true;
214     }
215     selinux_log(SELINUX_ERROR, "Get vendor version policy failed\n");
216     return false;
217 }
218 
GetPublicPolicy(std::string & publicPolicy)219 static bool GetPublicPolicy(std::string &publicPolicy)
220 {
221     if (access(PUBLIC_CIL, F_OK) == 0) {
222         publicPolicy = PUBLIC_CIL;
223         return true;
224     }
225     selinux_log(SELINUX_ERROR, "Get vendor public policy failed\n");
226     return false;
227 }
228 
WaitForChild(pid_t pid)229 static bool WaitForChild(pid_t pid)
230 {
231     int status = -1;
232     if (waitpid(pid, &status, 0) < 0) {
233         selinux_log(SELINUX_ERROR, "Waitpid failed\n");
234         return false;
235     }
236     if (WIFEXITED(status)) {
237         int exitCode = WEXITSTATUS(status);
238         selinux_log(SELINUX_INFO, "Child terminated by exit %d\n", exitCode);
239         if (exitCode == 0) {
240             return true;
241         }
242     } else if (WIFSIGNALED(status)) {
243         selinux_log(SELINUX_ERROR, "Child terminated by signal %d\n", WTERMSIG(status));
244     } else if (WIFSTOPPED(status)) {
245         selinux_log(SELINUX_ERROR, "Child stopped by signal %d\n", WSTOPSIG(status));
246     } else {
247         selinux_log(SELINUX_ERROR, "Child exit with status %d\n", status);
248     }
249     return false;
250 }
251 
CompilePolicyWithFork(std::vector<const char * > & compileCmd)252 static bool CompilePolicyWithFork(std::vector<const char *> &compileCmd)
253 {
254     int pipeFd[PIPE_NUM];
255     if (pipe(pipeFd) < 0) {
256         selinux_log(SELINUX_ERROR, "Create pipe failed, %d, %s\n", errno, strerror(errno));
257         return false;
258     }
259     pid_t pid = fork();
260     if (pid < 0) {
261         selinux_log(SELINUX_ERROR, "Fork subprocess failed, %d, %s\n", errno, strerror(errno));
262         (void)close(pipeFd[0]);
263         (void)close(pipeFd[1]);
264         return false;
265     }
266     if (pid == 0) {
267         (void)close(pipeFd[0]);
268         if (dup2(pipeFd[1], STDERR_FILENO) == -1) {
269             selinux_log(SELINUX_ERROR, "Dup2 failed, %d, %s\n", errno, strerror(errno));
270             (void)close(pipeFd[1]);
271             _exit(1);
272         }
273         (void)close(pipeFd[1]);
274         if (execv(compileCmd[0], const_cast<char **>(compileCmd.data())) == -1) {
275             selinux_log(SELINUX_ERROR, "Execv subprocess failed, %d, %s\n", errno, strerror(errno));
276             return false;
277         }
278         _exit(1);
279         return false;
280     }
281     (void)close(pipeFd[1]);
282 
283     FILE *fp = fdopen(pipeFd[0], "r");
284     if (fp != nullptr) {
285         char buf[BUFF_SIZE] = {0};
286         while (fgets(buf, sizeof(buf) - 1, fp) != nullptr) {
287             size_t n = strlen(buf);
288             if (n == 0) {
289                 continue;
290             }
291             if (buf[n - 1] == '\n') {
292                 buf[n - 1] = '\0';
293             }
294             if (strstr(buf, "Failed") != nullptr) {
295                 selinux_log(SELINUX_ERROR, "SELinux compile result: %s\n", buf);
296             }
297         }
298         fclose(fp);
299     }
300 
301     (void)close(pipeFd[0]);
302 
303     return WaitForChild(pid);
304 }
305 
CompilePolicy(void)306 static bool CompilePolicy(void)
307 {
308     std::vector<const char *> compileCmd = {
309         "/system/bin/secilc",
310         VENDOR_CIL,
311         "-m",
312         "-N",
313         "-M",
314         "true",
315         "-G",
316         "-c",
317         "31",
318         "-f",
319         "/sys/fs/selinux/null",
320         "-o",
321         COMPILE_OUTPUT_POLICY,
322     };
323     compileCmd.emplace_back(SYSTEM_CIL);
324     std::string versionPolicy;
325     if (GetVersionPolicy(versionPolicy)) {
326         selinux_log(SELINUX_WARNING, "Add policy %s\n", versionPolicy.c_str());
327         compileCmd.emplace_back(versionPolicy.c_str());
328     }
329     std::string publicPolicy;
330     if (GetPublicPolicy(publicPolicy)) {
331         selinux_log(SELINUX_WARNING, "Add policy %s\n", publicPolicy.c_str());
332         compileCmd.emplace_back(publicPolicy.c_str());
333     }
334     compileCmd.emplace_back(nullptr);
335 
336     return CompilePolicyWithFork(compileCmd);
337 }
338 
GetPolicyFile(std::string & policyFile)339 static bool GetPolicyFile(std::string &policyFile)
340 {
341     if (access(SYSTEM_CIL, R_OK) != 0) { // no system.cil file
342         policyFile = DEFAULT_POLICY;
343         selinux_log(SELINUX_WARNING, "No cil file found, load default policy\n");
344         return true;
345     }
346 
347     // check precompiled policy
348     if (CompareHash(PRECOMPILED_POLICY_SYSTEM_CIL_HASH, SYSTEM_CIL_HASH)) {
349         if (access(PRECOMPILED_POLICY, R_OK) == 0) {
350             policyFile = PRECOMPILED_POLICY;
351             selinux_log(SELINUX_WARNING, "Found precompiled policy, load %s\n", policyFile.c_str());
352             return true;
353         }
354         if (access(BACKUP_PRECOMPILED_POLICY, R_OK) == 0) {
355             policyFile = BACKUP_PRECOMPILED_POLICY;
356             selinux_log(SELINUX_WARNING, "Found precompiled policy, load %s\n", policyFile.c_str());
357             return true;
358         }
359     }
360 
361     // no precompiled policy, compile from cil
362     selinux_log(SELINUX_WARNING, "No precompiled policy found, compile it\n");
363     if (CompilePolicy()) {
364         policyFile = COMPILE_OUTPUT_POLICY;
365         return true;
366     }
367     return false;
368 }
369 
LoadPolicyFromFile(const std::string & policyFile)370 static int LoadPolicyFromFile(const std::string &policyFile)
371 {
372     void *data = nullptr;
373     size_t size = 0;
374     if (!ReadPolicyFile(policyFile, &data, size)) {
375         return -1;
376     }
377     if (!LoadPolicy(data, size)) {
378         munmap(data, size);
379         return -1;
380     }
381     munmap(data, size);
382     return 0;
383 }
384 
LoadPolicy(void)385 int LoadPolicy(void)
386 {
387     InitSelinuxLog();
388     std::string policyFile;
389     if (!GetPolicyFile(policyFile)) {
390         return -1;
391     }
392     return LoadPolicyFromFile(policyFile);
393 }
394