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, &currentPos, &memPtr);
399     LoadParameterContexts(VENDOR_PARAMETER_CONTEXTS, &currentPos, &memPtr);
400     UnmapSharedMem((char *)head, SELINUX_PARAM_SPACE);
401     return currentPos > 0 ? 0 : -SELINUX_CONTEXTS_FILE_LOAD_ERROR;
402 }
403