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 #include "contexts_trie.h"
16 #include <ctype.h>
17 #include <stdbool.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include "selinux_error.h"
21 #include "selinux_share_mem.h"
22
23 static const char DEFAULT_CONTEXT[] = "u:object_r:default_param:s0";
24 static const int32_t DEFAULT_CONTEXT_INDEX = 0;
25 static const int32_t INIT_INDEX = 1;
26 static const size_t CONTEXTS_LENGTH_MIN = 16; // sizeof("x u:object_r:x:s0")
27 static const size_t CONTEXTS_LENGTH_MAX = 1024;
28 static const uint32_t SELINUX_PARAM_SPACE = 1024 * 80;
29 static const uint32_t MAX_LEN = 255;
30 static const char SYSTEM_PARAMETER_CONTEXTS[] = "/system/etc/selinux/targeted/contexts/parameter_contexts";
31 static const char VENDOR_PARAMETER_CONTEXTS[] = "/vendor/etc/selinux/targeted/contexts/parameter_contexts";
32
GetGroupNode(ParamContextsTrie * root,const char * name,uint32_t len)33 static ParamHashNode *GetGroupNode(ParamContextsTrie *root, const char *name, uint32_t len)
34 {
35 HashNode *node = HashMapGet(root->handle, name, len);
36 if (node == NULL) {
37 return NULL;
38 }
39 return HASHMAP_ENTRY(node, ParamHashNode, hashNode);
40 }
41
AddGroupNode(ParamContextsTrie * root,const char * name,ParamContextsTrie * child)42 static ParamHashNode *AddGroupNode(ParamContextsTrie *root, const char *name, ParamContextsTrie *child)
43 {
44 uint32_t nameLen = (uint32_t)strlen(name);
45 ParamHashNode *groupNode = GetGroupNode(root, name, nameLen);
46 if (groupNode != NULL) {
47 return groupNode;
48 }
49
50 groupNode = (ParamHashNode *)calloc(1, sizeof(ParamHashNode));
51 if (groupNode == NULL) {
52 return NULL;
53 }
54 groupNode->nameLen = nameLen;
55 groupNode->name = (char *)calloc(1, nameLen + 1);
56 if (groupNode->name == NULL) {
57 free(groupNode);
58 return NULL;
59 }
60 memcpy(groupNode->name, name, nameLen + 1);
61 groupNode->childPtr = child;
62
63 HashMapAdd(root->handle, &groupNode->hashNode);
64 return groupNode;
65 }
66
ReleaseParamContextsTrieNode(ParamContextsTrie ** node)67 static void ReleaseParamContextsTrieNode(ParamContextsTrie **node)
68 {
69 if (*node == NULL) {
70 return;
71 }
72 if ((*node)->handle != NULL) {
73 HashMapDestroy((*node)->handle);
74 }
75 free(*node);
76 *node = NULL;
77 }
78
InsertElementToTrie(ParamContextsTrie * root,const char * element,ParamContextsTrie ** child)79 static bool InsertElementToTrie(ParamContextsTrie *root, const char *element, ParamContextsTrie **child)
80 {
81 uint32_t nameLen = (uint32_t)strlen(element);
82 ParamHashNode *childNode = GetGroupNode(root, element, nameLen);
83 if (childNode != NULL) {
84 *child = childNode->childPtr;
85 return true;
86 }
87 ParamContextsTrie *childPtr = (ParamContextsTrie *)calloc(1, sizeof(ParamContextsTrie));
88 if (childPtr == NULL) {
89 return false;
90 }
91 childPtr->prefixLabel = DEFAULT_CONTEXT;
92 childPtr->matchLabel = DEFAULT_CONTEXT;
93 childPtr->labeled = UNLABELED;
94 childPtr->index = DEFAULT_CONTEXT_INDEX;
95 if (HashMapCreate(&childPtr->handle) != 0) {
96 ReleaseParamContextsTrieNode(&childPtr);
97 return false;
98 }
99 if (AddGroupNode(root, element, childPtr) == NULL) {
100 ReleaseParamContextsTrieNode(&childPtr);
101 return false;
102 }
103 *child = childPtr;
104 return true;
105 }
106
InsertParamToTrie(ParamContextsTrie * root,const char * paramPrefix,const char * contexts,int index)107 static bool InsertParamToTrie(ParamContextsTrie *root, const char *paramPrefix, const char *contexts, int index)
108 {
109 if (root == NULL || paramPrefix == NULL || contexts == NULL) {
110 return false;
111 }
112 char *tmpPrefix = strdup(paramPrefix);
113 if (tmpPrefix == NULL) {
114 return false;
115 }
116 char *rest = NULL;
117 char *element = strtok_r(tmpPrefix, ".", &rest);
118 while (element != NULL) {
119 ParamContextsTrie *child = NULL;
120 if (!InsertElementToTrie(root, element, &child)) {
121 free(tmpPrefix);
122 return false;
123 }
124 root = child;
125 element = strtok_r(NULL, ".", &rest);
126 }
127 if (paramPrefix[strlen(paramPrefix) - 1] == '.') {
128 root->prefixLabel = contexts;
129 root->labeled = PREFIX_LABELED;
130 } else {
131 root->matchLabel = contexts;
132 root->labeled = MATCH_LABELED;
133 }
134 root->index = index;
135 free(tmpPrefix);
136 return true;
137 }
138
SearchFromParamTrie(ParamContextsTrie * root,const char * paraName)139 const char *SearchFromParamTrie(ParamContextsTrie *root, const char *paraName)
140 {
141 const char *updateCurLabel = DEFAULT_CONTEXT;
142 const char *tmpName = paraName;
143 ParamHashNode *childNode = NULL;
144
145 const char *bar = strchr(tmpName, '.');
146 while (bar != NULL) {
147 childNode = GetGroupNode(root, tmpName, bar - tmpName);
148 if (childNode == NULL) {
149 goto nomatch;
150 }
151 if (root->labeled == PREFIX_LABELED) {
152 updateCurLabel = root->prefixLabel;
153 }
154
155 root = childNode->childPtr;
156 tmpName = bar + 1;
157 bar = strchr(tmpName, '.');
158 }
159
160 childNode = GetGroupNode(root, tmpName, strlen(tmpName));
161 if (childNode != NULL) {
162 ParamContextsTrie *match = childNode->childPtr;
163 if (match->labeled == MATCH_LABELED) {
164 return match->matchLabel;
165 }
166 }
167
168 nomatch:
169 if (root->labeled == PREFIX_LABELED) {
170 return root->prefixLabel;
171 }
172 return updateCurLabel;
173 }
174
GetLabelIndex(ParamContextsTrie * root,const char * paraName)175 int GetLabelIndex(ParamContextsTrie *root, const char *paraName)
176 {
177 int updateCurIndex = DEFAULT_CONTEXT_INDEX;
178 const char *tmpName = paraName;
179 ParamHashNode *childNode = NULL;
180
181 const char *bar = strchr(tmpName, '.');
182 while (bar != NULL) {
183 childNode = GetGroupNode(root, tmpName, bar - tmpName);
184 if (childNode == NULL) {
185 goto nomatch;
186 }
187 if (root->labeled == PREFIX_LABELED) {
188 updateCurIndex = root->index;
189 }
190
191 root = childNode->childPtr;
192 tmpName = bar + 1;
193 bar = strchr(tmpName, '.');
194 }
195
196 childNode = GetGroupNode(root, tmpName, strlen(tmpName));
197 if (childNode != NULL) {
198 ParamContextsTrie *match = childNode->childPtr;
199 if (match->labeled == MATCH_LABELED) {
200 return match->index;
201 }
202 }
203
204 nomatch:
205 if (root->labeled == PREFIX_LABELED) {
206 return root->index;
207 }
208 return updateCurIndex;
209 }
210
CouldSkip(const char * line)211 static bool CouldSkip(const char *line)
212 {
213 size_t len = strlen(line);
214 if (len < CONTEXTS_LENGTH_MIN || len > CONTEXTS_LENGTH_MAX) {
215 return true;
216 }
217 int i = 0;
218 while (isspace(line[i])) {
219 i++;
220 }
221 if (line[i] == '#') {
222 return true;
223 }
224 return false;
225 }
226
InsertContextsList(ParamContextsList ** head,const char * param,const char * context,int index)227 static bool InsertContextsList(ParamContextsList **head, const char *param, const char *context, int index)
228 {
229 if (head == NULL || param == NULL || context == NULL) {
230 return false;
231 }
232 ParamContextsList *node = (ParamContextsList *)calloc(1, sizeof(ParamContextsList));
233 if (node == NULL) {
234 return false;
235 }
236
237 node->info.paraName = param;
238 node->info.paraContext = context;
239 node->next = NULL;
240 node->info.index = index;
241 (*head)->next = node;
242 *head = (*head)->next;
243 return true;
244 }
245
FindContextFromList(const char * context,ParamContextsList * listHead)246 static int FindContextFromList(const char *context, ParamContextsList *listHead)
247 {
248 if ((context == NULL) || (listHead == NULL)) {
249 return DEFAULT_CONTEXT_INDEX;
250 }
251 ParamContextsList *tmpHead = listHead;
252 while (tmpHead != NULL) {
253 if (strcmp(tmpHead->info.paraContext, context) == 0) {
254 return tmpHead->info.index;
255 }
256 tmpHead = tmpHead->next;
257 }
258 return DEFAULT_CONTEXT_INDEX;
259 }
260
ReadParamFromSharedMemInit(ParamContextsTrie ** root,ParamContextsList ** listPtr,SharedMem ** memPtr)261 static bool ReadParamFromSharedMemInit(ParamContextsTrie **root, ParamContextsList **listPtr, SharedMem **memPtr)
262 {
263 SharedMem *tmpMemPtr = (SharedMem *)InitSharedMem("/dev/__parameters__/param_selinux", SELINUX_PARAM_SPACE, true);
264 if (tmpMemPtr == NULL) {
265 return false;
266 }
267 SharedMem *memHead = tmpMemPtr;
268 ParamContextsTrie *tmpRoot = (ParamContextsTrie *)calloc(1, sizeof(ParamContextsTrie));
269 if (tmpRoot == NULL) {
270 UnmapSharedMem((char *)memHead, SELINUX_PARAM_SPACE);
271 return false;
272 }
273 tmpRoot->prefixLabel = DEFAULT_CONTEXT;
274 tmpRoot->matchLabel = DEFAULT_CONTEXT;
275 tmpRoot->index = DEFAULT_CONTEXT_INDEX;
276 if (HashMapCreate(&tmpRoot->handle) != 0) {
277 ReleaseParamContextsTrieNode(&tmpRoot);
278 UnmapSharedMem((char *)memHead, SELINUX_PARAM_SPACE);
279 return false;
280 }
281 ParamContextsList *tmpListPtr = (ParamContextsList *)calloc(1, sizeof(ParamContextsList));
282 if (tmpListPtr == NULL) {
283 ReleaseParamContextsTrieNode(&tmpRoot);
284 UnmapSharedMem((char *)memHead, SELINUX_PARAM_SPACE);
285 return false;
286 }
287 *root = tmpRoot;
288 *listPtr = tmpListPtr;
289 *memPtr = memHead;
290 return true;
291 }
292
ReadParamFromSharedMem(ParamContextsTrie ** trieRoot,ParamContextsList ** listHead)293 bool ReadParamFromSharedMem(ParamContextsTrie **trieRoot, ParamContextsList **listHead)
294 {
295 SharedMem *memPtr = NULL;
296 ParamContextsTrie *root = NULL;
297 ParamContextsList *listPtr = NULL;
298 if (!ReadParamFromSharedMemInit(&root, &listPtr, &memPtr)) {
299 return false;
300 }
301 uint32_t currentPos = 0;
302 ParamContextsList *tmpHead = listPtr;
303 int index = INIT_INDEX;
304 while (memPtr != NULL && memPtr->paramNameSize > 0) {
305 char *paramName = ReadSharedMem(memPtr->data, memPtr->paramNameSize);
306 char *context = ReadSharedMem(memPtr->data + memPtr->paramNameSize + 1, memPtr->paramLabelSize);
307 int tmpIndex = FindContextFromList(context, tmpHead->next);
308 tmpIndex = (tmpIndex == DEFAULT_CONTEXT_INDEX) ? index++ : tmpIndex;
309 if ((!InsertParamToTrie(root, paramName, context, tmpIndex)) ||
310 (!InsertContextsList(&listPtr, paramName, context, tmpIndex))) {
311 continue;
312 }
313 uint32_t dataLen = memPtr->paramNameSize + memPtr->paramLabelSize + 2; // 2 bytes for '\0'
314 uint32_t readSize = dataLen + sizeof(SharedMem); // space used for read SharedMem struct
315 currentPos += readSize;
316 if (currentPos > SELINUX_PARAM_SPACE) { // no space to read
317 break;
318 }
319 memPtr = (SharedMem *)((char *)memPtr + readSize);
320 }
321 listPtr = tmpHead->next;
322 free(tmpHead);
323 *listHead = listPtr;
324 *trieRoot = root;
325 return true;
326 }
327
WriteParamToSharedMem(char * paramName,char * context,uint32_t * currentPos,SharedMem ** memPtr)328 static int WriteParamToSharedMem(char *paramName, char *context, uint32_t *currentPos, SharedMem **memPtr)
329 {
330 uint32_t paramLen = strlen(paramName);
331 uint32_t contextLen = strlen(context);
332 if (paramLen > MAX_LEN || contextLen > MAX_LEN) { // too long, ignore
333 return 0;
334 }
335 uint32_t dataLen = paramLen + contextLen + 2; // 2 bytes for write '\0'
336 uint32_t writeSize = dataLen + sizeof(SharedMem); // space used for write SharedMem struct
337 if (*currentPos + writeSize > SELINUX_PARAM_SPACE) { // no space to write
338 return -1;
339 }
340 *currentPos += writeSize;
341
342 SharedMem *tmPtr = *memPtr;
343 tmPtr->paramNameSize = paramLen;
344 tmPtr->paramLabelSize = contextLen;
345 char *writePtr = tmPtr->data;
346 WriteSharedMem(writePtr, paramName, paramLen);
347 writePtr[paramLen] = '\0';
348 writePtr = tmPtr->data + paramLen + 1;
349 WriteSharedMem(writePtr, context, contextLen);
350 writePtr[contextLen] = '\0';
351 *memPtr = (SharedMem *)((char *)tmPtr + writeSize); // get the next SharedMem ptr
352 return 0;
353 }
354
LoadParameterContexts(const char * fileName,uint32_t * currentPos,SharedMem ** memPtr)355 static void LoadParameterContexts(const char* fileName, uint32_t *currentPos, SharedMem **memPtr)
356 {
357 char buffer[512] = {0};
358 FILE *fp = fopen(fileName, "r");
359 if (fp == NULL) {
360 return;
361 }
362 while (fgets(buffer, sizeof(buffer) - 1, fp) != NULL) {
363 size_t n = strlen(buffer);
364 if (n == 0) {
365 continue;
366 }
367 if (buffer[n - 1] == '\n') {
368 buffer[n - 1] = '\0';
369 }
370 if (CouldSkip(buffer)) {
371 continue;
372 }
373 char *rest = NULL;
374 char split[] = " \t";
375 char *paramName = strtok_r(buffer, split, &rest);
376 if (paramName == NULL) {
377 continue;
378 }
379 char *context = strtok_r(NULL, split, &rest);
380 if (context == NULL) {
381 continue;
382 }
383 if (WriteParamToSharedMem(paramName, context, currentPos, memPtr) != 0) {
384 break;
385 }
386 }
387 (void)fclose(fp);
388 }
389
LoadParameterContextsToSharedMem(void)390 int LoadParameterContextsToSharedMem(void)
391 {
392 SharedMem *memPtr = (SharedMem *)InitSharedMem("/dev/__parameters__/param_selinux", SELINUX_PARAM_SPACE, false);
393 if (memPtr == NULL) {
394 return -SELINUX_PTR_NULL;
395 }
396 SharedMem *head = memPtr;
397 uint32_t currentPos = 0;
398 LoadParameterContexts(SYSTEM_PARAMETER_CONTEXTS, ¤tPos, &memPtr);
399 LoadParameterContexts(VENDOR_PARAMETER_CONTEXTS, ¤tPos, &memPtr);
400 UnmapSharedMem((char *)head, SELINUX_PARAM_SPACE);
401 return currentPos > 0 ? 0 : -SELINUX_CONTEXTS_FILE_LOAD_ERROR;
402 }
403