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