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 "paraperm_checker.h"
17 #include <fcntl.h>
18 #include <fstream>
19 #include <regex>
20 #include <securec.h>
21 #include <selinux_internal.h>
22 #include <sstream>
23 #include <string>
24 #include <sys/socket.h>
25 #include <unistd.h>
26 #include "callbacks.h"
27 #include "selinux_error.h"
28 #include "selinux_klog.h"
29 #include "contexts_trie.h"
30
31 using namespace Selinux;
32
33 namespace {
34 static const std::string PARAMETER_CONTEXTS_FILE = "/system/etc/selinux/targeted/contexts/parameter_contexts";
35 static const std::string TYPE_PREFIX = "u:object_r:";
36 static const char *DEFAULT_CONTEXT = "u:object_r:default_param:s0";
37 static pthread_once_t FC_ONCE = PTHREAD_ONCE_INIT;
38 static std::unique_ptr<ParamContextsTrie> g_contextsTrie = nullptr;
39 static ParamContextsList *g_contextsList = nullptr;
40 static const int CONTEXTS_LENGTH_MIN = 16; // sizeof("x u:object_r:x:s0")
41 static const int CONTEXTS_LENGTH_MAX = 1024;
42 } // namespace
43
44 struct AuditMsg {
45 const ucred *ucred;
46 const char *name;
47 };
48
SelinuxAuditCallback(void * data,security_class_t cls,char * buf,size_t len)49 static int SelinuxAuditCallback(void *data, security_class_t cls, char *buf, size_t len)
50 {
51 if (data == nullptr || buf == nullptr) {
52 return -1;
53 }
54 auto *msg = reinterpret_cast<AuditMsg *>(data);
55 if (!msg->name || !msg->ucred) {
56 selinux_log(SELINUX_ERROR, "Selinux audit msg invalid argument\n");
57 return -1;
58 }
59 if (snprintf_s(buf, len, len - 1, "parameter=%s pid=%d uid=%d gid=%d", msg->name, msg->ucred->pid, msg->ucred->uid,
60 msg->ucred->gid) <= 0) {
61 return -1;
62 }
63 return 0;
64 }
65
SelinuxSetCallback()66 static void SelinuxSetCallback()
67 {
68 union selinux_callback cb;
69 cb.func_log = SelinuKLog;
70 selinux_set_callback(SELINUX_CB_LOG, cb);
71 cb.func_audit = SelinuxAuditCallback;
72 selinux_set_callback(SELINUX_CB_AUDIT, cb);
73 }
74
ReleaseMem()75 static void ReleaseMem()
76 {
77 DestroyParamList(&g_contextsList);
78 if (g_contextsTrie != nullptr) {
79 g_contextsTrie->Clear();
80 g_contextsTrie = nullptr;
81 }
82 }
83
CheckParaNameValid(const char * paraName)84 static int CheckParaNameValid(const char *paraName)
85 {
86 if (paraName == nullptr) {
87 return -SELINUX_PTR_NULL;
88 }
89 std::string para = paraName;
90 if (para.empty() || para[0] == '.' || para[para.size() - 1] == '.' || para.find("..") != std::string::npos) {
91 return -SELINUX_ARG_INVALID;
92 }
93 std::regex paraReg("^[a-zA-Z0-9_\\-@:](([\\.]{0,1})[a-zA-Z0-9_\\-@:])*");
94 if (std::regex_match(para, paraReg)) {
95 return SELINUX_SUCC;
96 }
97 return -SELINUX_ARG_INVALID;
98 }
99
CouldSkip(const std::string & line)100 static bool CouldSkip(const std::string &line)
101 {
102 if (line.size() < CONTEXTS_LENGTH_MIN || line.size() > CONTEXTS_LENGTH_MAX) {
103 return true;
104 }
105 int i = 0;
106 while (isspace(line[i])) {
107 i++;
108 }
109 if (line[i] == '#') {
110 return true;
111 }
112 if (line.find(TYPE_PREFIX) == line.npos) {
113 return true;
114 }
115 return false;
116 }
117
StartWith(const std::string & dst,const std::string & prefix)118 static bool StartWith(const std::string &dst, const std::string &prefix)
119 {
120 return dst.compare(0, prefix.size(), prefix) == 0;
121 }
122
DecodeString(const std::string & line)123 static struct ParameterInfo DecodeString(const std::string &line)
124 {
125 std::stringstream input(line);
126 struct ParameterInfo contextBuff = {"", ""};
127 std::string name;
128 if (input >> name) {
129 contextBuff.paraName = name;
130 }
131 std::string context;
132 if (input >> context) {
133 if (StartWith(context, TYPE_PREFIX)) {
134 contextBuff.paraContext = context;
135 }
136 }
137 return contextBuff;
138 }
139
InsertContextsList(const ParameterInfo & tmpInfo,ParamContextsList ** head)140 static bool InsertContextsList(const ParameterInfo &tmpInfo, ParamContextsList **head)
141 {
142 ParamContextsList *node = (ParamContextsList *)malloc(sizeof(ParamContextsList));
143 if (node == nullptr) {
144 return false;
145 }
146 struct ParameterNode contextBuff = {nullptr, nullptr};
147 contextBuff.paraName = strdup(tmpInfo.paraName.c_str());
148 contextBuff.paraContext = strdup(tmpInfo.paraContext.c_str());
149 node->info = contextBuff;
150 node->next = nullptr;
151 (*head)->next = node;
152 *head = (*head)->next;
153 return true;
154 }
155
ParameterContextsLoad()156 static bool ParameterContextsLoad()
157 {
158 ReleaseMem();
159 std::ifstream contextsFile(PARAMETER_CONTEXTS_FILE);
160 if (!contextsFile) {
161 selinux_log(SELINUX_ERROR, "Load parameter_contexts fail, no such file: %s\n", PARAMETER_CONTEXTS_FILE.c_str());
162 return false;
163 }
164 g_contextsTrie = std::make_unique<ParamContextsTrie>();
165 g_contextsList = (ParamContextsList *)malloc(sizeof(ParamContextsList));
166 if (g_contextsList == nullptr) {
167 selinux_log(SELINUX_ERROR, "malloc param info list head fail\n");
168 return false;
169 }
170 ParamContextsList *head = g_contextsList;
171 int lineNum = 0;
172 std::string line;
173 while (getline(contextsFile, line)) {
174 lineNum++;
175 if (CouldSkip(line))
176 continue;
177 struct ParameterInfo tmpInfo = DecodeString(line);
178 if (tmpInfo.paraContext.empty() || tmpInfo.paraName.empty()) {
179 selinux_log(SELINUX_ERROR, "parameter_contexts read fail in line %d\n", lineNum);
180 continue;
181 }
182 if (!g_contextsTrie->Insert(tmpInfo.paraName, tmpInfo.paraContext)) {
183 selinux_log(SELINUX_ERROR, "insert contexts trie node fail\n");
184 contextsFile.close();
185 ReleaseMem();
186 return false;
187 }
188 if (!InsertContextsList(tmpInfo, &head)) {
189 selinux_log(SELINUX_ERROR, "malloc param info list node fail\n");
190 contextsFile.close();
191 ReleaseMem();
192 return false;
193 }
194 }
195 head = g_contextsList->next;
196 free(g_contextsList);
197 g_contextsList = head;
198 selinux_log(SELINUX_INFO, "Load parameter_contexts succes: %s\n", PARAMETER_CONTEXTS_FILE.c_str());
199 contextsFile.close();
200 return true;
201 }
202
CheckPerm(const std::string & paraName,const char * srcContext,const char * destContext,const ucred & uc)203 static int CheckPerm(const std::string ¶Name, const char *srcContext, const char *destContext, const ucred &uc)
204 {
205 if (srcContext == nullptr || destContext == nullptr) {
206 selinux_log(SELINUX_INFO, "context empty!\n");
207 return -SELINUX_PTR_NULL;
208 }
209 selinux_log(SELINUX_INFO, "srcContext[%s] is setting param[%s] destContext[%s]\n", srcContext, paraName.c_str(),
210 destContext);
211 AuditMsg msg;
212 msg.name = paraName.c_str();
213 msg.ucred = &uc;
214 int res = selinux_check_access(srcContext, destContext, "parameter_service", "set", &msg);
215 return res == 0 ? SELINUX_SUCC : -SELINUX_PERMISSION_DENY;
216 }
217
SetSelinuxLogCallback()218 void SetSelinuxLogCallback()
219 {
220 __selinux_once(FC_ONCE, SelinuxSetCallback);
221 return;
222 }
223
DestroyParamList(ParamContextsList ** list)224 void DestroyParamList(ParamContextsList **list)
225 {
226 if (list == nullptr) {
227 return;
228 }
229 ParamContextsList *tmpNode;
230 ParamContextsList *listHead = *list;
231 while (listHead != nullptr) {
232 tmpNode = listHead->next;
233 free(listHead->info.paraName);
234 listHead->info.paraName = nullptr;
235 free(listHead->info.paraContext);
236 listHead->info.paraContext = nullptr;
237 free(listHead);
238 listHead = tmpNode;
239 }
240 *list = nullptr;
241 return;
242 }
243
GetParamList()244 ParamContextsList *GetParamList()
245 {
246 if (g_contextsList == nullptr) {
247 if (!ParameterContextsLoad()) {
248 return nullptr;
249 }
250 }
251 return g_contextsList;
252 }
253
GetParamLabel(const char * paraName,char ** context)254 int GetParamLabel(const char *paraName, char **context)
255 {
256 if (paraName == nullptr || context == nullptr) {
257 return -SELINUX_PTR_NULL;
258 }
259
260 int ret = CheckParaNameValid(paraName);
261 if (ret != 0) {
262 selinux_log(SELINUX_ERROR, "paraName invalid!\n");
263 return ret;
264 }
265
266 if (g_contextsTrie == nullptr) {
267 if (!ParameterContextsLoad()) {
268 return -SELINUX_CONTEXTS_FILE_LOAD_ERROR;
269 }
270 }
271
272 if (!g_contextsTrie->Search(std::string(paraName), context)) {
273 return -SELINUX_KEY_NOT_FOUND;
274 }
275 selinux_log(SELINUX_INFO, "find context: %s\n", *context);
276 return SELINUX_SUCC;
277 }
278
ReadParamCheck(const char * paraName)279 int ReadParamCheck(const char *paraName)
280 {
281 int ret = CheckParaNameValid(paraName);
282 if (ret != 0) {
283 selinux_log(SELINUX_ERROR, "paraName invalid!\n");
284 return ret;
285 }
286
287 char *srcContext = nullptr;
288 int rc = getcon(&srcContext);
289 if (rc < 0) {
290 selinux_log(SELINUX_ERROR, "getcon failed!\n");
291 return -SELINUX_GET_CONTEXT_ERROR;
292 }
293
294 AuditMsg msg;
295 msg.name = paraName;
296 ucred uc = {.pid = getpid(), .uid = getuid(), .gid = getgid()};
297 msg.ucred = &uc;
298 char *destContext = nullptr;
299 if (GetParamLabel(paraName, &destContext) != 0) {
300 destContext = strdup(DEFAULT_CONTEXT);
301 }
302 if (srcContext == nullptr || destContext == nullptr) {
303 freecon(srcContext);
304 return -SELINUX_PTR_NULL;
305 }
306 selinux_log(SELINUX_INFO, "srcContext[%s] is reading param[%s] destContext[%s]\n", srcContext, paraName,
307 destContext);
308 int res = selinux_check_access(srcContext, destContext, "file", "read", &msg);
309 freecon(srcContext);
310 free(destContext);
311 return res == 0 ? SELINUX_SUCC : -SELINUX_PERMISSION_DENY;
312 }
313
SetParamCheck(const char * paraName,struct ucred * uc)314 int SetParamCheck(const char *paraName, struct ucred *uc)
315 {
316 if (paraName == nullptr || uc == nullptr) {
317 return -SELINUX_PTR_NULL;
318 }
319
320 int ret = CheckParaNameValid(paraName);
321 if (ret != 0) {
322 selinux_log(SELINUX_ERROR, "paraName invalid!\n");
323 return ret;
324 }
325
326 char *srcContext = nullptr;
327
328 int rc = getpidcon(uc->pid, &srcContext);
329 if (rc < 0) {
330 selinux_log(SELINUX_ERROR, "getpidcon failed!\n");
331 return -SELINUX_GET_CONTEXT_ERROR;
332 }
333 char *destContext = nullptr;
334 if (GetParamLabel(paraName, &destContext) != 0) {
335 destContext = strdup(DEFAULT_CONTEXT);
336 }
337 int res = CheckPerm(std::string(paraName), srcContext, destContext, *uc);
338 freecon(srcContext);
339 free(destContext);
340 return res;
341 }
342