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, ¤tPos, &memPtr) != 0) {
317 break;
318 }
319 }
320 UnmapSharedMem((char *)head, SELINUX_PARAM_SPACE);
321 (void)fclose(fp);
322 return 0;
323 }
324