• 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 #include <selinux/label.h>
18 #include <selinux_internal.h>
19 #include <sstream>
20 #include <fstream>
21 #include "selinux_klog.h"
22 #include "selinux_error.h"
23 #include "callbacks.h"
24 #include "securec.h"
25 
26 using namespace Selinux;
27 
28 namespace {
29 static const std::string SERVICE_CONTEXTS_FILE = "/system/etc/selinux/targeted/contexts/service_contexts";
30 static const std::string HDF_SERVICE_CONTEXTS_FILE = "/system/etc/selinux/targeted/contexts/hdf_service_contexts";
31 static const std::string OBJECT_PREFIX = "u:object_r:";
32 static const int CONTEXTS_LENGTH_MIN = 16; // sizeof("x u:object_r:x:s0")
33 static const int CONTEXTS_LENGTH_MAX = 1024;
34 static pthread_once_t FC_ONCE = PTHREAD_ONCE_INIT;
35 } // namespace
36 
37 struct AuditMsg {
38     pid_t pid;
39     const char *name;
40 };
41 
SelinuxAuditCallback(void * data,security_class_t cls,char * buf,size_t len)42 static int SelinuxAuditCallback(void *data, security_class_t cls, char *buf, size_t len)
43 {
44     if (data == nullptr || buf == nullptr) {
45         return -1;
46     }
47     auto *msg = reinterpret_cast<AuditMsg *>(data);
48     if (!msg->name) {
49         selinux_log(SELINUX_ERROR, "audit msg invalid argument\n");
50         return -1;
51     }
52     if (snprintf_s(buf, len, len - 1, "service=%s pid=%d", msg->name, msg->pid) <= 0) {
53         return -1;
54     }
55     return 0;
56 }
57 
SelinuxSetCallback()58 static void SelinuxSetCallback()
59 {
60     union selinux_callback cb;
61     cb.func_log = SelinuKLog;
62     selinux_set_callback(SELINUX_CB_LOG, cb);
63     cb.func_audit = SelinuxAuditCallback;
64     selinux_set_callback(SELINUX_CB_AUDIT, cb);
65 }
66 
CouldSkip(const std::string & line)67 static bool CouldSkip(const std::string &line)
68 {
69     if (line.size() < CONTEXTS_LENGTH_MIN || line.size() > CONTEXTS_LENGTH_MAX) {
70         return true;
71     }
72     int i = 0;
73     while (isspace(line[i])) {
74         i++;
75     }
76     if (line[i] == '#') {
77         return true;
78     }
79     if (line.find(OBJECT_PREFIX) == line.npos) {
80         return true;
81     }
82     return false;
83 }
84 
StartWith(const std::string & dst,const std::string & prefix)85 static bool StartWith(const std::string &dst, const std::string &prefix)
86 {
87     return dst.compare(0, prefix.size(), prefix) == 0;
88 }
89 
DecodeString(const std::string & line)90 static struct ServiceInfo DecodeString(const std::string &line)
91 {
92     std::stringstream input(line);
93     struct ServiceInfo contextBuff = {"", ""};
94     std::string name;
95     if (input >> name) {
96         contextBuff.serviceName = name;
97     }
98     std::string context;
99     if (input >> context) {
100         if (StartWith(context, OBJECT_PREFIX)) {
101             contextBuff.serviceContext = context;
102         }
103     }
104     return contextBuff;
105 }
106 
CheckServiceNameValid(const std::string & serviceName)107 static int CheckServiceNameValid(const std::string &serviceName)
108 {
109     if (serviceName.empty() || serviceName[0] == '.') {
110         return -SELINUX_ARG_INVALID;
111     }
112     return SELINUX_SUCC;
113 }
114 
SetSelinuxLogCallback()115 void ServiceChecker::SetSelinuxLogCallback()
116 {
117     SetSelinuKLogLevel(SELINUX_KINFO);
118     __selinux_once(FC_ONCE, SelinuxSetCallback);
119     return;
120 }
121 
ServiceContextsLoad()122 bool ServiceChecker::ServiceContextsLoad()
123 {
124     // load service_contexts file
125     std::string name;
126     if (isHdf_) {
127         name = HDF_SERVICE_CONTEXTS_FILE;
128     } else {
129         name = SERVICE_CONTEXTS_FILE;
130     }
131     std::ifstream contextsFile(name);
132     if (contextsFile) {
133         int lineNum = 0;
134         std::string line;
135         while (getline(contextsFile, line)) {
136             lineNum++;
137             if (CouldSkip(line))
138                 continue;
139             struct ServiceInfo tmpInfo = DecodeString(line);
140             if (!tmpInfo.serviceContext.empty() && !tmpInfo.serviceName.empty()) {
141                 serviceMap.emplace(tmpInfo.serviceName, tmpInfo);
142             } else {
143                 selinux_log(SELINUX_ERROR, "service_contexts read fail in line %d\n", lineNum);
144             }
145         }
146     } else {
147         selinux_log(SELINUX_ERROR, "Load service_contexts fail, no such file: %s\n", name.c_str());
148         return false;
149     }
150     selinux_log(SELINUX_INFO, "Load service_contexts succes: %s\n", name.c_str());
151     contextsFile.close();
152     return true;
153 }
154 
GetServiceContext(const std::string & serviceName,std::string & context)155 int ServiceChecker::GetServiceContext(const std::string &serviceName, std::string &context)
156 {
157     if (CheckServiceNameValid(serviceName) != 0) {
158         selinux_log(SELINUX_ERROR, "serviceName invalid!\n");
159         return -SELINUX_ARG_INVALID;
160     }
161 
162     if (serviceMap.empty()) {
163         if (!ServiceContextsLoad()) {
164             return -SELINUX_CONTEXTS_FILE_LOAD_ERROR;
165         }
166     }
167 
168     auto iter = serviceMap.find(serviceName);
169     if (iter != serviceMap.end()) {
170         selinux_log(SELINUX_INFO, "find context: %s\n", iter->second.serviceContext.c_str());
171         context = iter->second.serviceContext;
172         return SELINUX_SUCC;
173     }
174     selinux_log(SELINUX_ERROR, "service %s's context not found!\n", serviceName.c_str());
175     return -SELINUX_KEY_NOT_FOUND;
176 }
177 
GetCallingContext(const pid_t & pid,std::string & context)178 static int GetCallingContext(const pid_t &pid, std::string &context)
179 {
180     char *srcContext = nullptr;
181     int rc = getpidcon(pid, &srcContext);
182     if (rc < 0) {
183         selinux_log(SELINUX_ERROR, "getpidcon failed!\n");
184         return -SELINUX_GET_CONTEXT_ERROR;
185     }
186     context = std::string(srcContext);
187     freecon(srcContext);
188     return SELINUX_SUCC;
189 }
190 
GetThisContext(std::string & context)191 static int GetThisContext(std::string &context)
192 {
193     char *con = nullptr;
194     int rc = getcon(&con);
195     if (rc < 0) {
196         selinux_log(SELINUX_ERROR, "getcon failed!\n");
197         return -SELINUX_GET_CONTEXT_ERROR;
198     }
199     context = std::string(con);
200     freecon(con);
201     return SELINUX_SUCC;
202 }
203 
CheckPerm(const pid_t & callingPid,const std::string & serviceName,std::string action)204 int ServiceChecker::CheckPerm(const pid_t &callingPid, const std::string &serviceName, std::string action)
205 {
206     std::string srcContext = "";
207     int ret = GetCallingContext(callingPid, srcContext);
208     if (ret < 0) {
209         return ret;
210     }
211     if (security_check_context(srcContext.c_str()) < 0) {
212         selinux_log(SELINUX_ERROR, "context: %s, %s\n", srcContext.c_str(), GetErrStr(SELINUX_CHECK_CONTEXT_ERROR));
213         return -SELINUX_CHECK_CONTEXT_ERROR;
214     }
215     std::string destContext = "";
216     if (action == "list") {
217         ret = GetThisContext(destContext);
218     } else {
219         ret = GetServiceContext(serviceName, destContext);
220     }
221     if (ret < 0) {
222         return ret;
223     }
224     if (security_check_context(destContext.c_str()) < 0) {
225         selinux_log(SELINUX_ERROR, "context: %s, %s\n", destContext.c_str(), GetErrStr(SELINUX_CHECK_CONTEXT_ERROR));
226         return -SELINUX_CHECK_CONTEXT_ERROR;
227     }
228 
229     AuditMsg msg;
230     msg.name = serviceName.c_str();
231     msg.pid = callingPid;
232     selinux_log(SELINUX_INFO, "srcContext[%s] %s service[%s] destContext[%s]\n", srcContext.c_str(), action.c_str(),
233                 msg.name, destContext.c_str());
234     int res =
235         selinux_check_access(srcContext.c_str(), destContext.c_str(), serviceClass_.c_str(), action.c_str(), &msg);
236     return res == 0 ? SELINUX_SUCC : -SELINUX_PERMISSION_DENY;
237 }
238 
ListServiceCheck(const pid_t & callingPid)239 int ServiceChecker::ListServiceCheck(const pid_t &callingPid)
240 {
241     return CheckPerm(callingPid, serviceClass_, "list");
242 }
243 
GetServiceCheck(const pid_t & callingPid,const std::string & serviceName)244 int ServiceChecker::GetServiceCheck(const pid_t &callingPid, const std::string &serviceName)
245 {
246     return CheckPerm(callingPid, serviceName, "get");
247 }
248 
GetRemoteServiceCheck(const pid_t & callingPid,const std::string & remoteServiceName)249 int ServiceChecker::GetRemoteServiceCheck(const pid_t &callingPid, const std::string &remoteServiceName)
250 {
251     if (isHdf_) {
252         selinux_log(SELINUX_ERROR, "hdf service has no permission to get remote!\n");
253         return -SELINUX_PERMISSION_DENY;
254     }
255     return CheckPerm(callingPid, remoteServiceName, "get_remote");
256 }
257 
AddServiceCheck(const pid_t & callingPid,const std::string & serviceName)258 int ServiceChecker::AddServiceCheck(const pid_t &callingPid, const std::string &serviceName)
259 {
260     return CheckPerm(callingPid, serviceName, "add");
261 }
262