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