1 /*
2 * Copyright (c) 2021 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 "param_manager.h"
17
18 #include <ctype.h>
19 #include <dlfcn.h>
20 #ifdef WITH_SELINUX
21 #include "selinux_parameter.h"
22 #endif
23
24 #if !defined PARAM_SUPPORT_SELINUX && !defined PARAM_SUPPORT_DAC
25 static ParamSecurityLabel g_defaultSecurityLabel;
26 #endif
27
GetParamSecurityOps(ParamWorkSpace * workSpace,int isInit)28 static int GetParamSecurityOps(ParamWorkSpace *workSpace, int isInit)
29 {
30 UNUSED(isInit);
31 #if (defined PARAM_SUPPORT_SELINUX || defined PARAM_SUPPORT_DAC)
32 int ret = RegisterSecurityOps(&workSpace->paramSecurityOps, isInit);
33 PARAM_CHECK(workSpace->paramSecurityOps.securityInitLabel != NULL, return -1, "Invalid securityInitLabel");
34 ret = workSpace->paramSecurityOps.securityInitLabel(&workSpace->securityLabel, isInit);
35 PARAM_CHECK(ret == 0, return PARAM_CODE_INVALID_NAME, "Failed to init security");
36 #else
37 workSpace->securityLabel = &g_defaultSecurityLabel;
38 workSpace->securityLabel->flags |= LABEL_ALL_PERMISSION;
39 #endif
40 return 0;
41 }
42
InitParamWorkSpace(ParamWorkSpace * workSpace,int onlyRead)43 int InitParamWorkSpace(ParamWorkSpace *workSpace, int onlyRead)
44 {
45 PARAM_CHECK(workSpace != NULL, return PARAM_CODE_INVALID_NAME, "Invalid param");
46 if (PARAM_TEST_FLAG(workSpace->flags, WORKSPACE_FLAGS_INIT)) {
47 return 0;
48 }
49 int isInit = 0;
50 int op = DAC_READ;
51 if (onlyRead == 0) {
52 isInit = LABEL_INIT_FOR_INIT;
53 op = DAC_WRITE;
54 }
55 int ret = GetParamSecurityOps(workSpace, isInit);
56 PARAM_CHECK(ret == 0, return -1, "Failed to get security operations");
57 ParamSecurityOps *paramSecurityOps = &workSpace->paramSecurityOps;
58 if (!LABEL_IS_ALL_PERMITTED(workSpace->securityLabel)) {
59 PARAM_CHECK(paramSecurityOps->securityFreeLabel != NULL, return -1, "Invalid securityFreeLabel");
60 PARAM_CHECK(paramSecurityOps->securityCheckFilePermission != NULL, return -1, "Invalid securityCheck");
61 PARAM_CHECK(paramSecurityOps->securityCheckParamPermission != NULL, return -1, "Invalid securityCheck");
62 if (isInit == LABEL_INIT_FOR_INIT) {
63 PARAM_CHECK(paramSecurityOps->securityGetLabel != NULL, return -1, "Invalid securityGetLabel");
64 PARAM_CHECK(paramSecurityOps->securityDecodeLabel != NULL, return -1, "Invalid securityDecodeLabel");
65 } else {
66 PARAM_CHECK(paramSecurityOps->securityEncodeLabel != NULL, return -1, "Invalid securityEncodeLabel");
67 }
68 ret = paramSecurityOps->securityCheckFilePermission(workSpace->securityLabel, PARAM_STORAGE_PATH, op);
69 PARAM_CHECK(ret == 0, return PARAM_CODE_INVALID_NAME, "No permission to read file %s", PARAM_STORAGE_PATH);
70 }
71 if (onlyRead) {
72 ret = InitWorkSpace(CLIENT_PARAM_STORAGE_PATH, &workSpace->paramSpace, onlyRead);
73 } else {
74 ret = InitWorkSpace(PARAM_STORAGE_PATH, &workSpace->paramSpace, onlyRead);
75 }
76 PARAM_CHECK(ret == 0, return PARAM_CODE_INVALID_NAME, "Failed to init workspace");
77 PARAM_SET_FLAG(workSpace->flags, WORKSPACE_FLAGS_INIT);
78 return ret;
79 }
80
CloseParamWorkSpace(ParamWorkSpace * workSpace)81 void CloseParamWorkSpace(ParamWorkSpace *workSpace)
82 {
83 PARAM_CHECK(workSpace != NULL, return, "Invalid workSpace");
84 CloseWorkSpace(&workSpace->paramSpace);
85 if (workSpace->paramSecurityOps.securityFreeLabel != NULL) {
86 workSpace->paramSecurityOps.securityFreeLabel(workSpace->securityLabel);
87 }
88 workSpace->flags = 0;
89 }
90
ReadCommitId(ParamNode * entry)91 static uint32_t ReadCommitId(ParamNode *entry)
92 {
93 uint32_t commitId = atomic_load_explicit(&entry->commitId, memory_order_acquire);
94 while (commitId & PARAM_FLAGS_MODIFY) {
95 futex_wait(&entry->commitId, commitId);
96 commitId = atomic_load_explicit(&entry->commitId, memory_order_acquire);
97 }
98 return commitId & PARAM_FLAGS_COMMITID;
99 }
100
ReadParamCommitId(const ParamWorkSpace * workSpace,ParamHandle handle,uint32_t * commitId)101 int ReadParamCommitId(const ParamWorkSpace *workSpace, ParamHandle handle, uint32_t *commitId)
102 {
103 PARAM_CHECK(workSpace != NULL && commitId != NULL, return PARAM_CODE_INVALID_PARAM, "Invalid workSpace");
104 ParamNode *entry = (ParamNode *)GetTrieNode(&workSpace->paramSpace, handle);
105 if (entry == NULL) {
106 return -1;
107 }
108 *commitId = ReadCommitId(entry);
109 return 0;
110 }
111
ReadParamWithCheck(const ParamWorkSpace * workSpace,const char * name,uint32_t op,ParamHandle * handle)112 int ReadParamWithCheck(const ParamWorkSpace *workSpace, const char *name, uint32_t op, ParamHandle *handle)
113 {
114 PARAM_CHECK(handle != NULL, return PARAM_CODE_INVALID_PARAM, "Invalid param handle");
115 PARAM_CHECK(workSpace != NULL && name != NULL, return PARAM_CODE_INVALID_PARAM, "Invalid param name");
116 *handle = -1;
117 #ifdef READ_CHECK
118 int ret = CheckParamPermission(workSpace, workSpace->securityLabel, name, op);
119 PARAM_CHECK(ret == 0, return ret, "Forbid to access parameter %s", name);
120 #endif
121 ParamTrieNode *node = FindTrieNode(&workSpace->paramSpace, name, strlen(name), NULL);
122 if (node != NULL && node->dataIndex != 0) {
123 *handle = node->dataIndex;
124 return 0;
125 } else if (node != NULL) {
126 return PARAM_CODE_NODE_EXIST;
127 }
128 return PARAM_CODE_NOT_FOUND;
129 }
130
ReadParamValue(const ParamWorkSpace * workSpace,ParamHandle handle,char * value,uint32_t * length)131 int ReadParamValue(const ParamWorkSpace *workSpace, ParamHandle handle, char *value, uint32_t *length)
132 {
133 PARAM_CHECK(workSpace != NULL && length != NULL, return PARAM_CODE_INVALID_PARAM, "Invalid param");
134 ParamNode *entry = (ParamNode *)GetTrieNode(&workSpace->paramSpace, handle);
135 if (entry == NULL) {
136 return -1;
137 }
138 if (value == NULL) {
139 *length = entry->valueLength + 1;
140 return 0;
141 }
142 PARAM_CHECK(*length > entry->valueLength, return PARAM_CODE_INVALID_PARAM,
143 "Invalid value len %u %u", *length, entry->valueLength);
144 uint32_t commitId = ReadCommitId(entry);
145 do {
146 int ret = memcpy_s(value, *length, entry->data + entry->keyLength + 1, entry->valueLength);
147 PARAM_CHECK(ret == EOK, return -1, "Failed to copy value");
148 value[entry->valueLength] = '\0';
149 *length = entry->valueLength;
150 } while (commitId != ReadCommitId(entry));
151 return 0;
152 }
153
ReadParamName(const ParamWorkSpace * workSpace,ParamHandle handle,char * name,uint32_t length)154 int ReadParamName(const ParamWorkSpace *workSpace, ParamHandle handle, char *name, uint32_t length)
155 {
156 PARAM_CHECK(workSpace != NULL && name != NULL, return PARAM_CODE_INVALID_PARAM, "Invalid param");
157 ParamNode *entry = (ParamNode *)GetTrieNode(&workSpace->paramSpace, handle);
158 if (entry == NULL) {
159 return -1;
160 }
161 PARAM_CHECK(length > entry->keyLength, return -1, "Invalid param size %u %u", entry->keyLength, length);
162 int ret = memcpy_s(name, length, entry->data, entry->keyLength);
163 PARAM_CHECK(ret == EOK, return PARAM_CODE_INVALID_PARAM, "Failed to copy name");
164 name[entry->keyLength] = '\0';
165 return 0;
166 }
167
CheckParamName(const char * name,int info)168 int CheckParamName(const char *name, int info)
169 {
170 PARAM_CHECK(name != NULL, return PARAM_CODE_INVALID_PARAM, "Invalid param");
171 size_t nameLen = strlen(name);
172 if (nameLen >= PARAM_NAME_LEN_MAX) {
173 return PARAM_CODE_INVALID_NAME;
174 }
175 if (strcmp(name, "#") == 0) {
176 return 0;
177 }
178
179 if (nameLen < 1 || name[0] == '.' || (!info && name[nameLen - 1] == '.')) {
180 PARAM_LOGE("CheckParamName %s %d", name, info);
181 return PARAM_CODE_INVALID_NAME;
182 }
183
184 /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
185 /* Don't allow ".." to appear in a param name */
186 for (size_t i = 0; i < nameLen; i++) {
187 if (name[i] == '.') {
188 if (name[i - 1] == '.') {
189 return PARAM_CODE_INVALID_NAME;
190 }
191 continue;
192 }
193 if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') {
194 continue;
195 }
196 if (isalnum(name[i])) {
197 continue;
198 }
199 return PARAM_CODE_INVALID_NAME;
200 }
201 return 0;
202 }
203
ProcessParamTraversal(const WorkSpace * workSpace,const ParamTrieNode * node,void * cookie)204 static int ProcessParamTraversal(const WorkSpace *workSpace, const ParamTrieNode *node, void *cookie)
205 {
206 UNUSED(workSpace);
207 ParamTraversalContext *context = (ParamTraversalContext *)cookie;
208 ParamTrieNode *current = (ParamTrieNode *)node;
209 if (current == NULL) {
210 return 0;
211 }
212 if (current->dataIndex == 0) {
213 return 0;
214 }
215 ParamNode *entry = (ParamNode *)GetTrieNode(workSpace, current->dataIndex);
216 if (entry == NULL) {
217 return 0;
218 }
219 if ((strcmp("#", context->prefix) != 0) &&
220 (strncmp(entry->data, context->prefix, strlen(context->prefix)) != 0)) {
221 return 0;
222 }
223 context->traversalParamPtr(current->dataIndex, context->context);
224 return 0;
225 }
226
TraversalParam(const ParamWorkSpace * workSpace,const char * prefix,TraversalParamPtr walkFunc,void * cookie)227 int TraversalParam(const ParamWorkSpace *workSpace,
228 const char *prefix, TraversalParamPtr walkFunc, void *cookie)
229 {
230 PARAM_CHECK(workSpace != NULL && walkFunc != NULL, return PARAM_CODE_INVALID_PARAM, "Invalid param");
231 ParamTraversalContext context = {
232 walkFunc, cookie, prefix
233 };
234 ParamTrieNode *root = FindTrieNode(&workSpace->paramSpace, prefix, strlen(prefix), NULL);
235 PARAM_LOGV("TraversalParam prefix %s", prefix);
236 return TraversalTrieNode(&workSpace->paramSpace, root, ProcessParamTraversal, &context);
237 }
238
239 #ifdef WITH_SELINUX
240 static void *g_selinuxHandle = NULL;
CheckParamPermissionWithSelinux(const ParamSecurityLabel * srcLabel,const char * name,uint32_t mode)241 static int CheckParamPermissionWithSelinux(const ParamSecurityLabel *srcLabel, const char *name, uint32_t mode)
242 {
243 static void (*setSelinuxLogCallback)();
244 static int (*setParamCheck)(const char *paraName, struct ucred *uc);
245 g_selinuxHandle = dlopen("/system/lib/libparaperm_checker.z.so", RTLD_LAZY);
246 if (g_selinuxHandle == NULL) {
247 PARAM_LOGE("Failed to dlopen libparaperm_checker.z.so, %s\n", dlerror());
248 return DAC_RESULT_FORBIDED;
249 }
250 if (setSelinuxLogCallback == NULL) {
251 setSelinuxLogCallback = (void (*)())dlsym(g_selinuxHandle, "SetSelinuxLogCallback");
252 if (setSelinuxLogCallback == NULL) {
253 PARAM_LOGE("Failed to dlsym setSelinuxLogCallback, %s\n", dlerror());
254 return DAC_RESULT_FORBIDED;
255 }
256 }
257 (*setSelinuxLogCallback)();
258
259 if (setParamCheck == NULL) {
260 setParamCheck = (int (*)(const char *paraName, struct ucred *uc))dlsym(g_selinuxHandle, "SetParamCheck");
261 if (setParamCheck == NULL) {
262 PARAM_LOGE("Failed to dlsym setParamCheck, %s\n", dlerror());
263 return DAC_RESULT_FORBIDED;
264 }
265 }
266 struct ucred uc;
267 uc.pid = srcLabel->cred.pid;
268 uc.uid = srcLabel->cred.uid;
269 uc.gid = srcLabel->cred.gid;
270 int ret = setParamCheck(name, &uc);
271 if (ret != 0) {
272 PARAM_LOGI("Selinux check name %s pid %d uid %d %d result %d", name, uc.pid, uc.uid, uc.gid, ret);
273 }
274 return ret;
275 }
276 #endif
277
CheckParamPermission(const ParamWorkSpace * workSpace,const ParamSecurityLabel * srcLabel,const char * name,uint32_t mode)278 int CheckParamPermission(const ParamWorkSpace *workSpace,
279 const ParamSecurityLabel *srcLabel, const char *name, uint32_t mode)
280 {
281 PARAM_CHECK(workSpace != NULL && workSpace->securityLabel != NULL,
282 return PARAM_CODE_INVALID_PARAM, "Invalid param");
283 if (LABEL_IS_ALL_PERMITTED(workSpace->securityLabel)) {
284 return 0;
285 }
286 PARAM_CHECK(name != NULL && srcLabel != NULL, return -1, "Invalid param");
287 #ifdef WITH_SELINUX
288 if (mode == DAC_WRITE) {
289 int ret = CheckParamPermissionWithSelinux(srcLabel, name, mode);
290 if (ret != DAC_RESULT_PERMISSION) {
291 return DAC_RESULT_PERMISSION;
292 }
293 }
294 #endif
295 if (workSpace->paramSecurityOps.securityCheckParamPermission == NULL) {
296 return DAC_RESULT_FORBIDED;
297 }
298 uint32_t labelIndex = 0;
299 FindTrieNode(&workSpace->paramSpace, name, strlen(name), &labelIndex);
300 ParamSecruityNode *node = (ParamSecruityNode *)GetTrieNode(&workSpace->paramSpace, labelIndex);
301 PARAM_CHECK(node != NULL, return DAC_RESULT_FORBIDED, "Can not get security label %d", labelIndex);
302
303 ParamAuditData auditData = {};
304 auditData.name = name;
305 auditData.dacData.uid = node->uid;
306 auditData.dacData.gid = node->gid;
307 auditData.dacData.mode = node->mode;
308 auditData.label = node->data;
309 return workSpace->paramSecurityOps.securityCheckParamPermission(srcLabel, &auditData, mode);
310 }
311
DumpTrieDataNodeTraversal(const WorkSpace * workSpace,const ParamTrieNode * node,void * cookie)312 static int DumpTrieDataNodeTraversal(const WorkSpace *workSpace, const ParamTrieNode *node, void *cookie)
313 {
314 int verbose = *(int *)cookie;
315 ParamTrieNode *current = (ParamTrieNode *)node;
316 if (current == NULL) {
317 return 0;
318 }
319 if (verbose) {
320 PARAM_DUMP("\tTrie node info [%u,%u,%u] data: %u label: %u key length:%d \n\t key: %s \n",
321 current->left, current->right, current->child,
322 current->dataIndex, current->labelIndex, current->length, current->key);
323 }
324 if (current->dataIndex != 0) {
325 ParamNode *entry = (ParamNode *)GetTrieNode(workSpace, current->dataIndex);
326 if (entry != NULL) {
327 PARAM_DUMP("\tparameter length info [%d, %d] \n\t param: %s \n",
328 entry->keyLength, entry->valueLength, (entry != NULL) ? entry->data : "null");
329 }
330 }
331 if (current->labelIndex != 0 && verbose) {
332 ParamSecruityNode *label = (ParamSecruityNode *)GetTrieNode(workSpace, current->labelIndex);
333 if (label != NULL) {
334 PARAM_DUMP("\tparameter label dac %d %d %o \n\t label: %s \n",
335 label->uid, label->gid, label->mode, (label->length > 0) ? label->data : "null");
336 }
337 }
338 return 0;
339 }
340
DumpWorkSpace(const ParamWorkSpace * workSpace,int verbose)341 static void DumpWorkSpace(const ParamWorkSpace *workSpace, int verbose)
342 {
343 PARAM_DUMP("workSpace information \n");
344 PARAM_DUMP(" map file: %s \n", workSpace->paramSpace.fileName);
345 if (workSpace->paramSpace.area != NULL) {
346 PARAM_DUMP(" total size: %d \n", workSpace->paramSpace.area->dataSize);
347 PARAM_DUMP(" first offset: %d \n", workSpace->paramSpace.area->firstNode);
348 PARAM_DUMP(" current offset: %d \n", workSpace->paramSpace.area->currOffset);
349 PARAM_DUMP(" total node: %d \n", workSpace->paramSpace.area->trieNodeCount);
350 PARAM_DUMP(" total param node: %d \n", workSpace->paramSpace.area->paramNodeCount);
351 PARAM_DUMP(" total security node: %d\n", workSpace->paramSpace.area->securityNodeCount);
352 }
353 PARAM_DUMP(" node info: \n");
354 TraversalTrieNode(&workSpace->paramSpace, NULL, DumpTrieDataNodeTraversal, (void *)&verbose);
355 }
356
DumpParameters(const ParamWorkSpace * workSpace,int verbose)357 void DumpParameters(const ParamWorkSpace *workSpace, int verbose)
358 {
359 PARAM_CHECK(workSpace != NULL && workSpace->securityLabel != NULL, return, "Invalid param");
360 PARAM_DUMP("Dump all paramters begin ...\n");
361 DumpWorkSpace(workSpace, verbose);
362 if (verbose) {
363 PARAM_DUMP("Local sercurity information\n");
364 PARAM_DUMP("\t pid: %d uid: %d gid: %d \n",
365 workSpace->securityLabel->cred.pid,
366 workSpace->securityLabel->cred.uid,
367 workSpace->securityLabel->cred.gid);
368 }
369 PARAM_DUMP("Dump all paramters finish\n");
370 }
371