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