• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 "service_checker.h"
17 
18 #include <cctype>
19 #include <cerrno>
20 #include <cstdarg>
21 #include <cstddef>
22 #include <fstream>
23 #include <istream>
24 #include <mutex>
25 #include <sstream>
26 #include <streambuf>
27 #include <string>
28 #include <unordered_map>
29 #include <utility>
30 
31 #include <pthread.h>
32 #include "selinux/selinux.h"
33 
34 #include "src/callbacks.h"
35 #include "securec.h"
36 
37 #include "selinux_error.h"
38 #include "selinux_log.h"
39 
40 using namespace Selinux;
41 
42 namespace {
43 static const std::string OBJECT_PREFIX = "u:object_r:";
44 static const std::string DEFAULT_CONTEXT = "u:object_r:default_service:s0";
45 static const std::string DEFAULT_HDF_CONTEXT = "u:object_r:default_hdf_service:s0";
46 static const int CONTEXTS_LENGTH_MIN = 16; // sizeof("x u:object_r:x:s0")
47 static const int CONTEXTS_LENGTH_MAX = 1024;
48 static pthread_once_t FC_ONCE = PTHREAD_ONCE_INIT;
49 static std::unordered_map<std::string, struct ServiceInfo> g_serviceMap;
50 std::mutex g_selinuxLock;
51 std::mutex g_loadContextsLock;
52 static const std::vector<std::string> SERVICE_CONTEXTS_FILE = {
53     "/system/etc/selinux/targeted/contexts/service_contexts",
54     "/vendor/etc/selinux/targeted/contexts/service_contexts",
55 };
56 static const std::vector<std::string> HDF_SERVICE_CONTEXTS_FILE = {
57     "/system/etc/selinux/targeted/contexts/hdf_service_contexts",
58     "/vendor/etc/selinux/targeted/contexts/hdf_service_contexts",
59 };
60 } // namespace
61 
HdfListServiceCheck(pid_t callingPid)62 extern "C" int HdfListServiceCheck(pid_t callingPid)
63 {
64     return ServiceChecker::GetInstance().ListServiceCheck(callingPid);
65 }
66 
HdfGetServiceCheck(pid_t callingPid,const char * serviceName)67 extern "C" int HdfGetServiceCheck(pid_t callingPid, const char *serviceName)
68 {
69     if (serviceName == nullptr) {
70         return -SELINUX_PTR_NULL;
71     }
72     return ServiceChecker::GetInstance().GetServiceCheck(callingPid, serviceName);
73 }
74 
HdfAddServiceCheck(pid_t callingPid,const char * serviceName)75 extern "C" int HdfAddServiceCheck(pid_t callingPid, const char *serviceName)
76 {
77     if (serviceName == nullptr) {
78         return -SELINUX_PTR_NULL;
79     }
80     return ServiceChecker::GetInstance().AddServiceCheck(callingPid, serviceName);
81 }
82 
83 struct AuditMsg {
84     pid_t pid;
85     const char *name;
86 };
87 
SelinuxAuditCallback(void * data,security_class_t cls,char * buf,size_t len)88 static int SelinuxAuditCallback(void *data, security_class_t cls, char *buf, size_t len)
89 {
90     if (data == nullptr || buf == nullptr) {
91         return -1;
92     }
93     auto *msg = reinterpret_cast<AuditMsg *>(data);
94     if (!msg->name) {
95         selinux_log(SELINUX_ERROR, "audit msg invalid argument\n");
96         return -1;
97     }
98     if (snprintf_s(buf, len, len - 1, "service=%s pid=%d", msg->name, msg->pid) <= 0) {
99         return -1;
100     }
101     return 0;
102 }
103 
SelinuxSetCallback()104 static void SelinuxSetCallback()
105 {
106     SetSelinuxHilogLevel(SELINUX_HILOG_ERROR);
107     union selinux_callback cb;
108     cb.func_log = SelinuxHilog;
109     selinux_set_callback(SELINUX_CB_LOG, cb);
110     cb.func_audit = SelinuxAuditCallback;
111     selinux_set_callback(SELINUX_CB_AUDIT, cb);
112 }
113 
CouldSkip(const std::string & line)114 static bool CouldSkip(const std::string &line)
115 {
116     if (line.size() < CONTEXTS_LENGTH_MIN || line.size() > CONTEXTS_LENGTH_MAX) {
117         return true;
118     }
119     int i = 0;
120     while (isspace(line[i])) {
121         i++;
122     }
123     if (line[i] == '#') {
124         return true;
125     }
126     if (line.find(OBJECT_PREFIX) == line.npos) {
127         return true;
128     }
129     return false;
130 }
131 
StartWith(const std::string & dst,const std::string & prefix)132 static bool StartWith(const std::string &dst, const std::string &prefix)
133 {
134     return dst.compare(0, prefix.size(), prefix) == 0;
135 }
136 
DecodeString(const std::string & line)137 static struct ServiceInfo DecodeString(const std::string &line)
138 {
139     std::stringstream input(line);
140     struct ServiceInfo contextBuff = {"", ""};
141     std::string name;
142     if (input >> name) {
143         contextBuff.serviceName = name;
144     }
145     std::string context;
146     if (input >> context) {
147         if (StartWith(context, OBJECT_PREFIX)) {
148             contextBuff.serviceContext = context;
149         }
150     }
151     return contextBuff;
152 }
153 
CheckServiceNameValid(const std::string & serviceName)154 static int CheckServiceNameValid(const std::string &serviceName)
155 {
156     if (serviceName.empty() || serviceName[0] == '.') {
157         return -SELINUX_ARG_INVALID;
158     }
159     return SELINUX_SUCC;
160 }
161 
ServiceContextsLoad(const std::vector<std::string> & fileName)162 static bool ServiceContextsLoad(const std::vector<std::string> &fileName)
163 {
164     // load service_contexts file
165     for (const auto &file : fileName) {
166         std::ifstream contextsFile(file);
167         if (!contextsFile) {
168             selinux_log(SELINUX_ERROR, "Load service_contexts fail, no such file: %s\n", file.c_str());
169             continue;
170         }
171         int lineNum = 0;
172         std::string line;
173         while (getline(contextsFile, line)) {
174             lineNum++;
175             if (CouldSkip(line)) {
176                 continue;
177             }
178             struct ServiceInfo tmpInfo = DecodeString(line);
179             if (!tmpInfo.serviceContext.empty() && !tmpInfo.serviceName.empty()) {
180                 g_serviceMap.emplace(tmpInfo.serviceName, tmpInfo);
181             } else {
182                 selinux_log(SELINUX_ERROR, "service_contexts read fail in line %d\n", lineNum);
183             }
184         }
185         selinux_log(SELINUX_INFO, "Load service_contexts success: %s\n", file.c_str());
186         contextsFile.close();
187     }
188     return !g_serviceMap.empty();
189 }
190 
ServiceChecker(bool isHdf)191 ServiceChecker::ServiceChecker(bool isHdf) : isHdf_(isHdf)
192 {
193     if (isHdf) {
194         serviceClass_ = "hdf_devmgr_class";
195     } else {
196         serviceClass_ = "samgr_class";
197     }
198     __selinux_once(FC_ONCE, SelinuxSetCallback);
199 }
200 
GetServiceContext(const std::string & serviceName,std::string & context)201 int ServiceChecker::GetServiceContext(const std::string &serviceName, std::string &context)
202 {
203     if (CheckServiceNameValid(serviceName) != 0) {
204         selinux_log(SELINUX_ERROR, "serviceName invalid!\n");
205         return -SELINUX_ARG_INVALID;
206     }
207     {
208         std::lock_guard<std::mutex> lock(g_loadContextsLock);
209         if (g_serviceMap.empty()) {
210             if (!ServiceContextsLoad(isHdf_ ? HDF_SERVICE_CONTEXTS_FILE : SERVICE_CONTEXTS_FILE)) {
211                 return -SELINUX_CONTEXTS_FILE_LOAD_ERROR;
212             }
213         }
214     }
215 
216     auto iter = g_serviceMap.find(serviceName);
217     if (iter != g_serviceMap.end()) {
218         context = iter->second.serviceContext;
219     } else {
220         context = isHdf_ ? DEFAULT_HDF_CONTEXT : DEFAULT_CONTEXT;
221     }
222 
223     return SELINUX_SUCC;
224 }
225 
GetCallingContext(const pid_t pid,std::string & context)226 static int GetCallingContext(const pid_t pid, std::string &context)
227 {
228     char *srcContext = nullptr;
229     int rc = getpidcon(pid, &srcContext);
230     if (rc < 0) {
231         selinux_log(SELINUX_ERROR, "getpidcon failed: %s\n", strerror(errno));
232         return -SELINUX_GET_CONTEXT_ERROR;
233     }
234     context = std::string(srcContext);
235     freecon(srcContext);
236     return SELINUX_SUCC;
237 }
238 
GetThisContext(std::string & context)239 static int GetThisContext(std::string &context)
240 {
241     char *con = nullptr;
242     int rc = getcon(&con);
243     if (rc < 0) {
244         selinux_log(SELINUX_ERROR, "getcon failed!\n");
245         return -SELINUX_GET_CONTEXT_ERROR;
246     }
247     context = std::string(con);
248     freecon(con);
249     return SELINUX_SUCC;
250 }
251 
CheckPerm(const pid_t callingPid,const std::string & serviceName,std::string action)252 int ServiceChecker::CheckPerm(const pid_t callingPid, const std::string &serviceName, std::string action)
253 {
254     std::string srcContext = "";
255     int ret = GetCallingContext(callingPid, srcContext);
256     if (ret != SELINUX_SUCC) {
257         return ret;
258     }
259     if (security_check_context(srcContext.c_str()) < 0) {
260         selinux_log(SELINUX_ERROR, "context: %s, %s\n", srcContext.c_str(), GetErrStr(SELINUX_CHECK_CONTEXT_ERROR));
261         return -SELINUX_CHECK_CONTEXT_ERROR;
262     }
263     std::string destContext = "";
264     if (action == "list") {
265         ret = GetThisContext(destContext);
266     } else {
267         ret = GetServiceContext(serviceName, destContext);
268     }
269     if (ret != SELINUX_SUCC) {
270         return ret;
271     }
272     if (security_check_context(destContext.c_str()) < 0) {
273         selinux_log(SELINUX_ERROR, "context: %s, %s\n", destContext.c_str(), GetErrStr(SELINUX_CHECK_CONTEXT_ERROR));
274         return -SELINUX_CHECK_CONTEXT_ERROR;
275     }
276 
277     AuditMsg msg;
278     msg.name = serviceName.c_str();
279     msg.pid = callingPid;
280     {
281         std::lock_guard<std::mutex> lock(g_selinuxLock);
282         ret =  selinux_check_access(srcContext.c_str(), destContext.c_str(),
283             serviceClass_.c_str(), action.c_str(), &msg);
284     }
285     return ret == 0 ? SELINUX_SUCC : -SELINUX_PERMISSION_DENY;
286 }
287 
ListServiceCheck(const pid_t callingPid)288 int ServiceChecker::ListServiceCheck(const pid_t callingPid)
289 {
290     return CheckPerm(callingPid, serviceClass_, "list");
291 }
292 
GetServiceCheck(const pid_t callingPid,const std::string & serviceName)293 int ServiceChecker::GetServiceCheck(const pid_t callingPid, const std::string &serviceName)
294 {
295     return CheckPerm(callingPid, serviceName, "get");
296 }
297 
GetRemoteServiceCheck(const pid_t callingPid,const std::string & remoteServiceName)298 int ServiceChecker::GetRemoteServiceCheck(const pid_t callingPid, const std::string &remoteServiceName)
299 {
300     if (isHdf_) {
301         selinux_log(SELINUX_ERROR, "hdf service has no permission to get remote!\n");
302         return -SELINUX_PERMISSION_DENY;
303     }
304     return CheckPerm(callingPid, remoteServiceName, "get_remote");
305 }
306 
AddServiceCheck(const pid_t callingPid,const std::string & serviceName)307 int ServiceChecker::AddServiceCheck(const pid_t callingPid, const std::string &serviceName)
308 {
309     return CheckPerm(callingPid, serviceName, "add");
310 }
311 
GetInstance()312 ServiceChecker& ServiceChecker::GetInstance()
313 {
314     static ServiceChecker instance(true);
315     return instance;
316 }
317