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 "param_base.h"
16
17#include <ctype.h>
18#include <errno.h>
19#include <limits.h>
20
21#include "init_param.h"
22#ifndef STARTUP_INIT_TEST
23#include "param_include.h"
24#endif
25#include "param_manager.h"
26#include "param_security.h"
27#include "param_trie.h"
28
29#define PUBLIC_APP_BEGIN_UID 10000
30
31static ParamWorkSpace g_paramWorkSpace = {0};
32
33static int CheckParamPermission_(const ParamLabelIndex *labelIndex,
34    const ParamSecurityLabel *srcLabel, const char *name, uint32_t mode);
35STATIC_INLINE int CheckAndExtendSpace(ParamWorkSpace *workSpace, const char *name, uint32_t labelIndex);
36STATIC_INLINE ParamTrieNode *BaseFindTrieNode(WorkSpace *workSpace,
37    const char *key, uint32_t keyLen, uint32_t *matchLabel);
38STATIC_INLINE const char *CachedParameterCheck(CachedParameter *param, int *changed);
39
40static int InitParamSecurity(ParamWorkSpace *workSpace,
41    RegisterSecurityOpsPtr registerOps, ParamSecurityType type, int isInit, int op)
42{
43    PARAM_CHECK(workSpace != NULL && type < PARAM_SECURITY_MAX, return -1, "Invalid param");
44    registerOps(&workSpace->paramSecurityOps[type], isInit);
45    PARAM_CHECK(workSpace->paramSecurityOps[type].securityInitLabel != NULL,
46        return -1, "Invalid securityInitLabel");
47    int ret = workSpace->paramSecurityOps[type].securityInitLabel(&workSpace->securityLabel, isInit);
48    PARAM_CHECK(ret == 0, return PARAM_CODE_INVALID_NAME, "Failed to init security");
49
50    ParamSecurityOps *paramSecurityOps = GetParamSecurityOps(type);
51    PARAM_CHECK(paramSecurityOps != NULL, return -1, "Invalid paramSecurityOps");
52    PARAM_CHECK(paramSecurityOps->securityFreeLabel != NULL, return -1, "Invalid securityFreeLabel");
53    PARAM_CHECK(paramSecurityOps->securityCheckFilePermission != NULL, return -1, "Invalid securityCheck");
54    if (isInit == LABEL_INIT_FOR_INIT) {
55        PARAM_CHECK(paramSecurityOps->securityGetLabel != NULL, return -1, "Invalid securityGetLabel");
56    }
57    ret = paramSecurityOps->securityCheckFilePermission(&workSpace->securityLabel, PARAM_STORAGE_PATH, op);
58    PARAM_CHECK(ret == 0, return PARAM_CODE_INVALID_NAME, "No permission to read file %s", PARAM_STORAGE_PATH);
59    PARAM_LOGV("Init parameter %s success", paramSecurityOps->name);
60    return 0;
61}
62
63INIT_LOCAL_API int RegisterSecurityOps(int onlyRead)
64{
65    int isInit = 0;
66    int op = DAC_READ;
67    if (onlyRead == 0) {
68        isInit = LABEL_INIT_FOR_INIT;
69        op = DAC_WRITE;
70    }
71    int ret = InitParamSecurity(&g_paramWorkSpace, RegisterSecurityDacOps, PARAM_SECURITY_DAC, isInit, op);
72    PARAM_CHECK(ret == 0, return -1, "Failed to get security operations");
73#ifdef PARAM_SUPPORT_SELINUX
74    ret = InitParamSecurity(&g_paramWorkSpace, RegisterSecuritySelinuxOps, PARAM_SECURITY_SELINUX, isInit, op);
75    PARAM_CHECK(ret == 0, return -1, "Failed to get security operations");
76#endif
77    return ret;
78}
79
80static int CheckNeedInit(int onlyRead, const PARAM_WORKSPACE_OPS *ops)
81{
82    if (ops != NULL) {
83        g_paramWorkSpace.ops.updaterMode = ops->updaterMode;
84        if (ops->getServiceGroupIdByPid != NULL) {
85            g_paramWorkSpace.ops.getServiceGroupIdByPid = ops->getServiceGroupIdByPid;
86        }
87        if (ops->logFunc != NULL) {
88            if (onlyRead == 0) {
89                g_paramWorkSpace.ops.logFunc = ops->logFunc;
90            } else if (g_paramWorkSpace.ops.logFunc == NULL) {
91                g_paramWorkSpace.ops.logFunc = ops->logFunc;
92            }
93        }
94#ifdef PARAM_SUPPORT_SELINUX
95        g_paramWorkSpace.ops.setfilecon = ops->setfilecon;
96#endif
97    }
98    if (PARAM_TEST_FLAG(g_paramWorkSpace.flags, WORKSPACE_FLAGS_INIT)) {
99        PARAM_LOGV("Param workspace has been init");
100        if (PARAM_TEST_FLAG(g_paramWorkSpace.flags, WORKSPACE_FLAGS_FOR_INIT)) {
101            return 0; // init has been init workspace, do not init
102        }
103        if (onlyRead == 0) { // init not init workspace, do init it
104            CloseParamWorkSpace();
105            return 1;
106        }
107        return 0;
108    }
109    if (onlyRead == 0) {
110        return 1;
111    }
112#ifdef STARTUP_INIT_TEST
113    // for ut, do not init workspace
114    char path[PATH_MAX] = { 0 };
115    (void)readlink("/proc/self/exe", path, sizeof(path) - 1);
116    char *name = strstr(path, "/init_unittest");
117    if (name != NULL) {
118        PARAM_LOGW("Can not init client for init_test");
119        return 0;
120    }
121#endif
122    return 1;
123}
124
125static int AllocSpaceMemory(uint32_t maxLabel)
126{
127    WorkSpace *workSpace = GetWorkSpace(WORKSPACE_INDEX_SIZE);
128    PARAM_CHECK(workSpace != NULL, return PARAM_CODE_ERROR, "Invalid dac workspace");
129    if (workSpace->area->spaceSizeOffset != 0) {
130        return 0;
131    }
132    ParamWorkSpace *paramSpace = GetParamWorkSpace();
133    PARAM_CHECK(paramSpace != NULL, return -1, "Invalid workspace");
134    uint32_t realLen = PARAM_ALIGN(sizeof(WorkSpaceSize) + sizeof(uint32_t) * maxLabel);
135    PARAM_CHECK((workSpace->area->currOffset + realLen) < workSpace->area->dataSize, return 0,
136        "Failed to allocate currOffset %u, dataSize %u datalen %u",
137        workSpace->area->currOffset, workSpace->area->dataSize, realLen);
138    WorkSpaceSize *node = (WorkSpaceSize *)(workSpace->area->data + workSpace->area->currOffset);
139    node->maxLabelIndex = maxLabel;
140    node->spaceSize[WORKSPACE_INDEX_DAC] = PARAM_WORKSPACE_DAC;
141    node->spaceSize[WORKSPACE_INDEX_BASE] = PARAM_WORKSPACE_MAX;
142    for (uint32_t i = WORKSPACE_INDEX_BASE + 1; i < maxLabel; i++) {
143        node->spaceSize[i] = PARAM_WORKSPACE_MIN;
144        PARAM_LOGV("AllocSpaceMemory spaceSize index %u %u", i, node->spaceSize[i]);
145        if (paramSpace->workSpace[i] != NULL) {
146            paramSpace->workSpace[i]->spaceSize = PARAM_WORKSPACE_MIN;
147        }
148    }
149    workSpace->area->spaceSizeOffset = workSpace->area->currOffset;
150    workSpace->area->currOffset += realLen;
151    return 0;
152}
153
154static int CreateWorkSpace(int onlyRead)
155{
156    int ret = 0;
157    ParamWorkSpace *paramSpace = GetParamWorkSpace();
158    PARAM_CHECK(paramSpace != NULL, return -1, "Invalid workspace");
159#ifdef PARAM_SUPPORT_SELINUX
160    ret = AddWorkSpace(WORKSPACE_NAME_DAC, WORKSPACE_INDEX_DAC, 0, PARAM_WORKSPACE_DAC);
161    PARAM_CHECK(ret == 0, return -1, "Failed to add dac workspace");
162    ret = AddWorkSpace(WORKSPACE_NAME_DEF_SELINUX, WORKSPACE_INDEX_BASE, onlyRead, PARAM_WORKSPACE_MAX);
163    PARAM_CHECK(ret == 0, return -1, "Failed to add default workspace");
164
165    // open dac workspace
166    ret = OpenWorkSpace(WORKSPACE_INDEX_DAC, onlyRead);
167    PARAM_CHECK(ret == 0, return -1, "Failed to open dac workspace");
168
169    // for other workspace
170    ParamSecurityOps *ops = GetParamSecurityOps(PARAM_SECURITY_SELINUX);
171    if (ops != NULL && ops->securityGetLabel != NULL) {
172        ret = ops->securityGetLabel("create");
173    }
174    paramSpace->maxLabelIndex++;
175#else
176    ret = AddWorkSpace(WORKSPACE_NAME_NORMAL, WORKSPACE_INDEX_DAC, onlyRead, PARAM_WORKSPACE_MAX);
177    PARAM_CHECK(ret == 0, return -1, "Failed to add dac workspace");
178    ret = OpenWorkSpace(WORKSPACE_INDEX_DAC, onlyRead);
179    PARAM_CHECK(ret == 0, return -1, "Failed to open dac workspace");
180    paramSpace->maxLabelIndex = 1;
181#endif
182    return ret;
183}
184
185INIT_INNER_API int InitParamWorkSpace(int onlyRead, const PARAM_WORKSPACE_OPS *ops)
186{
187    PARAM_ONLY_CHECK(CheckNeedInit(onlyRead, ops) != 0, return 0);
188
189    paramMutexEnvInit();
190    if (!PARAM_TEST_FLAG(g_paramWorkSpace.flags, WORKSPACE_FLAGS_INIT)) {
191        g_paramWorkSpace.maxSpaceCount = PARAM_DEF_SELINUX_LABEL;
192        g_paramWorkSpace.workSpace = (WorkSpace **)calloc(g_paramWorkSpace.maxSpaceCount, sizeof(WorkSpace *));
193        PARAM_CHECK(g_paramWorkSpace.workSpace != NULL, return -1, "Failed to alloc memory for workSpace");
194    }
195    PARAM_SET_FLAG(g_paramWorkSpace.flags, WORKSPACE_FLAGS_INIT);
196
197    int ret = RegisterSecurityOps(onlyRead);
198    PARAM_CHECK(ret == 0, return -1, "Failed to get security operations");
199    g_paramWorkSpace.checkParamPermission = CheckParamPermission_;
200    ret = CreateWorkSpace(onlyRead);
201    PARAM_CHECK(ret == 0, return -1, "Failed to create workspace");
202
203    if (onlyRead == 0) {
204        PARAM_LOGI("Max selinux label %u %u", g_paramWorkSpace.maxSpaceCount, g_paramWorkSpace.maxLabelIndex);
205        // alloc space size memory from dac
206        ret = AllocSpaceMemory(g_paramWorkSpace.maxLabelIndex);
207        PARAM_CHECK(ret == 0, return -1, "Failed to alloc space size");
208
209        // add default dac policy
210        ParamAuditData auditData = {0};
211        auditData.name = "#";
212        auditData.dacData.gid = DAC_DEFAULT_GROUP;
213        auditData.dacData.uid = DAC_DEFAULT_USER;
214        auditData.dacData.mode = DAC_DEFAULT_MODE; // 0774 default mode
215        auditData.dacData.paramType = PARAM_TYPE_STRING;
216        auditData.memberNum = 1;
217        auditData.members[0] = DAC_DEFAULT_GROUP;
218#ifdef PARAM_SUPPORT_SELINUX
219        auditData.selinuxIndex = INVALID_SELINUX_INDEX;
220#endif
221        ret = AddSecurityLabel(&auditData);
222        PARAM_CHECK(ret == 0, return ret, "Failed to add default dac label");
223        PARAM_SET_FLAG(g_paramWorkSpace.flags, WORKSPACE_FLAGS_FOR_INIT);
224    }
225    return ret;
226}
227
228INIT_LOCAL_API void CloseParamWorkSpace(void)
229{
230    PARAM_LOGI("CloseParamWorkSpace %x", g_paramWorkSpace.flags);
231    if (!PARAM_TEST_FLAG(g_paramWorkSpace.flags, WORKSPACE_FLAGS_INIT)) {
232        return;
233    }
234    for (uint32_t i = 0; i < g_paramWorkSpace.maxSpaceCount; i++) {
235        if (g_paramWorkSpace.workSpace[i] != NULL) {
236            CloseWorkSpace(g_paramWorkSpace.workSpace[i]);
237            free(g_paramWorkSpace.workSpace[i]);
238        }
239        g_paramWorkSpace.workSpace[i] = NULL;
240    }
241    free(g_paramWorkSpace.workSpace);
242    g_paramWorkSpace.workSpace = NULL;
243    for (int i = 0; i < PARAM_SECURITY_MAX; i++) {
244        if (g_paramWorkSpace.paramSecurityOps[i].securityFreeLabel != NULL) {
245            g_paramWorkSpace.paramSecurityOps[i].securityFreeLabel(&g_paramWorkSpace.securityLabel);
246        }
247    }
248    g_paramWorkSpace.flags = 0;
249}
250
251INIT_LOCAL_API void ParamWorBaseLog(InitLogLevel logLevel, uint32_t domain, const char *tag, const char *fmt, ...)
252{
253    if (g_paramWorkSpace.ops.logFunc != NULL) {
254        va_list vargs;
255        va_start(vargs, fmt);
256        g_paramWorkSpace.ops.logFunc(logLevel, domain, tag, fmt, vargs);
257        va_end(vargs);
258    }
259}
260
261INIT_INNER_API ParamWorkSpace *GetParamWorkSpace(void)
262{
263    return &g_paramWorkSpace;
264}
265
266void InitParameterClient(void)
267{
268    PARAM_WORKSPACE_OPS ops = {0};
269    ops.updaterMode = 0;
270    InitParamWorkSpace(1, &ops);
271}
272
273INIT_LOCAL_API int AddWorkSpace(const char *name, uint32_t labelIndex, int onlyRead, uint32_t spaceSize)
274{
275    ParamWorkSpace *paramSpace = GetParamWorkSpace();
276    PARAM_CHECK(paramSpace != NULL, return -1, "Invalid workspace");
277#ifdef PARAM_SUPPORT_SELINUX
278    const char *realName = name;
279#else
280    const char *realName = WORKSPACE_NAME_NORMAL;
281    labelIndex = 0;
282#endif
283    int ret = CheckAndExtendSpace(paramSpace, name, labelIndex);
284    PARAM_CHECK(ret == 0, return -1, "Not enough memory for %s", realName);
285    if (paramSpace->workSpace[labelIndex] != NULL) {
286        return 0;
287    }
288    const size_t size = strlen(realName) + 1;
289    WorkSpace *workSpace = (WorkSpace *)malloc(sizeof(WorkSpace) + size);
290    PARAM_CHECK(workSpace != NULL, return -1, "Failed to create workspace for %s", realName);
291    workSpace->flags = 0;
292    workSpace->spaceSize = spaceSize;
293    workSpace->area = NULL;
294    workSpace->spaceIndex = labelIndex;
295    ATOMIC_INIT(&workSpace->rwSpaceLock, 0);
296    PARAMSPACE_AREA_INIT_LOCK(workSpace);
297    ret = PARAM_STRCPY(workSpace->fileName, size, realName);
298    PARAM_CHECK(ret == 0, free(workSpace);
299        return -1, "Failed to copy file name %s", realName);
300    paramSpace->workSpace[labelIndex] = workSpace;
301    PARAM_LOGV("AddWorkSpace %s index %d onlyRead %s", paramSpace->workSpace[labelIndex]->fileName,
302        paramSpace->workSpace[labelIndex]->spaceIndex, onlyRead ? "true" : "false");
303
304    if (spaceSize != 0) {
305        return ret;
306    }
307    // get size
308    WorkSpaceSize *workSpaceSize = GetWorkSpaceSize(GetWorkSpace(WORKSPACE_INDEX_SIZE));
309    if (workSpaceSize != NULL) {
310        paramSpace->workSpace[labelIndex]->spaceSize = workSpaceSize->spaceSize[labelIndex];
311    }
312    return ret;
313}
314
315STATIC_INLINE int CheckAndExtendSpace(ParamWorkSpace *paramSpace, const char *name, uint32_t labelIndex)
316{
317    if (paramSpace->maxSpaceCount > labelIndex) {
318        return 0;
319    }
320    if (labelIndex >= PARAM_MAX_SELINUX_LABEL) {
321        PARAM_LOGE("Not enough memory for label index %u", labelIndex);
322        return -1;
323    }
324    PARAM_LOGW("Not enough memory for label index %u need to extend memory %u", labelIndex, paramSpace->maxSpaceCount);
325    WorkSpace **space = (WorkSpace **)calloc(sizeof(WorkSpace *),
326        paramSpace->maxSpaceCount + PARAM_DEF_SELINUX_LABEL);
327    PARAM_CHECK(space != NULL, return -1, "Failed to realloc memory for %s", name);
328    int ret = PARAM_MEMCPY(space, sizeof(WorkSpace *) * paramSpace->maxSpaceCount,
329        paramSpace->workSpace, sizeof(WorkSpace *) * paramSpace->maxSpaceCount);
330    PARAM_CHECK(ret == 0, free(space);
331        return -1, "Failed to copy memory for %s", name);
332    paramSpace->maxSpaceCount += PARAM_DEF_SELINUX_LABEL;
333    free(paramSpace->workSpace);
334    paramSpace->workSpace = space;
335    return 0;
336}
337
338INIT_LOCAL_API int OpenWorkSpace(uint32_t index, int readOnly)
339{
340    ParamWorkSpace *paramSpace = GetParamWorkSpace();
341    PARAM_CHECK(paramSpace != NULL && paramSpace->workSpace != NULL, return -1, "Invalid workspace index %u", index);
342    WorkSpace *workSpace = NULL;
343    PARAM_ONLY_CHECK(index >= paramSpace->maxSpaceCount, workSpace = paramSpace->workSpace[index]);
344    PARAM_CHECK(workSpace != NULL, return 0, "Invalid index %d", index);
345    int ret = 0;
346    uint32_t rwSpaceLock = ATOMIC_LOAD_EXPLICIT(&workSpace->rwSpaceLock, MEMORY_ORDER_ACQUIRE);
347    while (rwSpaceLock & WORKSPACE_STATUS_IN_PROCESS) {
348        futex_wait_private(&workSpace->rwSpaceLock, rwSpaceLock);
349        rwSpaceLock = ATOMIC_LOAD_EXPLICIT(&workSpace->rwSpaceLock, MEMORY_ORDER_ACQUIRE);
350    }
351
352    ATOMIC_STORE_EXPLICIT(&workSpace->rwSpaceLock, rwSpaceLock | WORKSPACE_STATUS_IN_PROCESS, MEMORY_ORDER_RELEASE);
353    if (workSpace->area == NULL) {
354        ret = InitWorkSpace(workSpace, readOnly, workSpace->spaceSize);
355        if (ret != 0) {
356            PARAM_LOGE("Forbid to open workspace for %s error %d", workSpace->fileName, errno);
357        }
358#ifndef PARAM_SUPPORT_SELINUX
359    }
360    ATOMIC_STORE_EXPLICIT(&workSpace->rwSpaceLock, rwSpaceLock & ~WORKSPACE_STATUS_IN_PROCESS, MEMORY_ORDER_RELEASE);
361#else
362    } else if (readOnly) {
363        if ((rwSpaceLock & WORKSPACE_STATUS_VALID) == WORKSPACE_STATUS_VALID) {
364            ret = 0;
365        } else if ((paramSpace->flags & WORKSPACE_FLAGS_NEED_ACCESS) == WORKSPACE_FLAGS_NEED_ACCESS) {
366            char buffer[FILENAME_LEN_MAX] = {0};
367            int size = PARAM_SPRINTF(buffer, sizeof(buffer), "%s/%s", PARAM_STORAGE_PATH, workSpace->fileName);
368            if (size > 0 && access(buffer, R_OK) == 0) {
369                PARAM_LOGW("Open workspace %s access ok ", workSpace->fileName);
370                rwSpaceLock |= WORKSPACE_STATUS_VALID;
371                ret = 0;
372            } else {
373                ret = -1;
374                PARAM_LOGE("Forbid to open workspace for %s error %d", workSpace->fileName, errno);
375                rwSpaceLock &= ~WORKSPACE_STATUS_VALID;
376            }
377        }
378    }
379    ATOMIC_STORE_EXPLICIT(&workSpace->rwSpaceLock, rwSpaceLock & ~WORKSPACE_STATUS_IN_PROCESS, MEMORY_ORDER_RELEASE);
380    futex_wake_private(&workSpace->rwSpaceLock, INT_MAX);
381#endif
382    return ret;
383}
384
385STATIC_INLINE int ReadParamWithCheck(WorkSpace **workspace, const char *name, uint32_t op, ParamTrieNode **node)
386{
387    ParamLabelIndex labelIndex = {0};
388    WorkSpace *dacSpace = g_paramWorkSpace.workSpace[0];
389    PARAM_CHECK(dacSpace != NULL && dacSpace->area != NULL,
390        return DAC_RESULT_FORBIDED, "Invalid workSpace for %s", name);
391    *node = BaseFindTrieNode(dacSpace, name, strlen(name), &labelIndex.dacLabelIndex);
392    labelIndex.workspace = GetWorkSpaceByName(name);
393    PARAM_CHECK(labelIndex.workspace != NULL, return DAC_RESULT_FORBIDED, "Invalid workSpace for %s", name);
394    labelIndex.selinuxLabelIndex = labelIndex.workspace->spaceIndex;
395
396    int ret = CheckParamPermission_(&labelIndex, &g_paramWorkSpace.securityLabel, name, op);
397    PARAM_CHECK(ret == 0, return ret, "Forbid to read parameter %s", name);
398#ifdef PARAM_SUPPORT_SELINUX
399    // search from real workspace
400    *node = BaseFindTrieNode(labelIndex.workspace, name, strlen(name), NULL);
401#endif
402    *workspace = labelIndex.workspace;
403    return ret;
404}
405
406static int CheckUserInGroup(WorkSpace *space, const ParamSecurityNode *node, uid_t uid)
407{
408    for (uint32_t i = 0; i < node->memberNum; i++) {
409        if (node->members[i] == uid) {
410            return 0;
411        }
412    }
413    return -1;
414}
415
416STATIC_INLINE int DacCheckGroupPermission(const ParamSecurityLabel *srcLabel, uint32_t mode, ParamSecurityNode *node)
417{
418    uint32_t localMode = (mode & (DAC_READ | DAC_WRITE | DAC_WATCH)) >> DAC_GROUP_START;
419    if (srcLabel->cred.gid == node->gid) {
420        if ((node->mode & localMode) != 0) {
421            return DAC_RESULT_PERMISSION;
422        }
423    }
424    if (mode != DAC_WRITE || g_paramWorkSpace.ops.getServiceGroupIdByPid == NULL) {
425        return DAC_RESULT_FORBIDED;
426    }
427    gid_t gids[64] = { 0 }; // max gid number
428    const uint32_t gidNumber = (uint32_t)g_paramWorkSpace.ops.getServiceGroupIdByPid(
429        srcLabel->cred.pid, gids, sizeof(gids) / sizeof(gids[0]));
430    for (uint32_t index = 0; index < gidNumber; index++) {
431        PARAM_LOGV("DacCheckGroupPermission gid %u", gids[index]);
432        if (gids[index] != node->gid) {
433            continue;
434        }
435        if ((node->mode & localMode) != 0) {
436            return DAC_RESULT_PERMISSION;
437        }
438    }
439    return DAC_RESULT_FORBIDED;
440}
441
442STATIC_INLINE int DacCheckParamPermission(const ParamLabelIndex *labelIndex,
443    const ParamSecurityLabel *srcLabel, const char *name, uint32_t mode)
444{
445#ifndef STARTUP_INIT_TEST
446    if (srcLabel->cred.uid == 0) {
447        return DAC_RESULT_PERMISSION;
448    }
449#endif
450    // get dac label
451    WorkSpace *space = g_paramWorkSpace.workSpace[WORKSPACE_INDEX_DAC];
452    ParamSecurityNode *node = (ParamSecurityNode *)GetTrieNode(space, labelIndex->dacLabelIndex);
453    PARAM_CHECK(node != NULL, return DAC_RESULT_FORBIDED, "Can not get security label %u selinuxLabelIndex %u for %s",
454        labelIndex->dacLabelIndex, labelIndex->selinuxLabelIndex, name);
455    /**
456     * DAC group
457     * user:group:read|write|watch
458     */
459    uint32_t localMode = (mode & (DAC_READ | DAC_WRITE | DAC_WATCH)) >> DAC_OTHER_START;
460    // 1, check other
461    if ((node->mode & localMode) != 0) {
462        return DAC_RESULT_PERMISSION;
463    }
464    // 2, check uid
465    if (srcLabel->cred.uid == node->uid) {
466        localMode = mode & (DAC_READ | DAC_WRITE | DAC_WATCH);
467        if ((node->mode & localMode) != 0) {
468            return DAC_RESULT_PERMISSION;
469        }
470    }
471    // 3, check gid
472    if (DacCheckGroupPermission(srcLabel, mode, node) == DAC_RESULT_PERMISSION) {
473        return DAC_RESULT_PERMISSION;
474    }
475    // 4, check user in group
476    if (CheckUserInGroup(space, node, srcLabel->cred.uid) == 0) {
477        localMode = (mode & (DAC_READ | DAC_WRITE | DAC_WATCH)) >> DAC_GROUP_START;
478        if ((node->mode & localMode) != 0) {
479            return DAC_RESULT_PERMISSION;
480        }
481    }
482    // forbid
483    PARAM_LOGW("Param '%s' label gid:%d uid:%d mode 0%x", name, srcLabel->cred.gid, srcLabel->cred.uid, mode);
484    PARAM_LOGW("Cfg label %u gid:%d uid:%d mode 0%x ", labelIndex->dacLabelIndex, node->gid, node->uid, node->mode);
485
486    int ret = DAC_RESULT_FORBIDED;
487#ifndef __MUSL__
488#ifndef STARTUP_INIT_TEST
489    ret = DAC_RESULT_PERMISSION;
490#endif
491#endif
492    return ret;
493}
494
495#ifdef PARAM_SUPPORT_SELINUX
496STATIC_INLINE const char *GetSelinuxContent(const char *name)
497{
498    SelinuxSpace *selinuxSpace = &g_paramWorkSpace.selinuxSpace;
499    const char *content = WORKSPACE_NAME_DEF_SELINUX;
500    if (selinuxSpace->getParamLabel != NULL) {
501        content = selinuxSpace->getParamLabel(name);
502    }
503    return content;
504}
505
506STATIC_INLINE int SelinuxCheckParamPermission(const ParamLabelIndex *labelIndex,
507    const ParamSecurityLabel *srcLabel, const char *name, uint32_t mode)
508{
509    SelinuxSpace *selinuxSpace = &g_paramWorkSpace.selinuxSpace;
510    int ret = SELINUX_RESULT_FORBIDED;
511    if (mode == DAC_WRITE) {
512        PARAM_CHECK(selinuxSpace->setParamCheck != NULL, return ret, "Invalid setParamCheck");
513        // check
514        SrcInfo info;
515        info.uc.pid = srcLabel->cred.pid;
516        info.uc.uid = srcLabel->cred.uid;
517        info.uc.gid = srcLabel->cred.gid;
518        info.sockFd = srcLabel->sockFd;
519        const char *context = GetSelinuxContent(name);
520        ret = selinuxSpace->setParamCheck(name, context, &info);
521    } else {
522#ifdef STARTUP_INIT_TEST
523        return selinuxSpace->readParamCheck(name);
524#endif
525        ret = OpenWorkSpace(labelIndex->selinuxLabelIndex, 1);
526    }
527    if (ret != 0) {
528        ret = SELINUX_RESULT_FORBIDED;
529        PARAM_LOGE("Selinux check name %s in %s info [%d %d %d] failed!",
530            name, GetSelinuxContent(name), srcLabel->cred.pid, srcLabel->cred.uid, srcLabel->cred.gid);
531    }
532    return ret;
533}
534#endif
535
536#if defined(STARTUP_INIT_TEST) || defined(__LITEOS_A__) || defined(__LITEOS_M__)
537static int CheckParamPermission_(const ParamLabelIndex *labelIndex,
538    const ParamSecurityLabel *srcLabel, const char *name, uint32_t mode)
539{
540    // for root, all permission, but for appspawn must to check
541    if (srcLabel->cred.uid == 0 && srcLabel->cred.pid == 1) {
542        return DAC_RESULT_PERMISSION;
543    }
544    int ret = DAC_RESULT_PERMISSION;
545    for (int i = 0; i < PARAM_SECURITY_MAX; i++) {
546        if (PARAM_TEST_FLAG(g_paramWorkSpace.securityLabel.flags[i], LABEL_ALL_PERMISSION)) {
547            continue;
548        }
549        ParamSecurityOps *ops = &g_paramWorkSpace.paramSecurityOps[i];
550        if (ops->securityCheckParamPermission == NULL) {
551            continue;
552        }
553        ret = ops->securityCheckParamPermission(labelIndex, srcLabel, name, mode);
554        if (ret == DAC_RESULT_FORBIDED) {
555            PARAM_LOGW("CheckParamPermission %s %s FORBID", ops->name, name);
556            break;
557        }
558    }
559    return ret;
560}
561#else
562static int CheckParamPermission_(const ParamLabelIndex *labelIndex,
563    const ParamSecurityLabel *srcLabel, const char *name, uint32_t mode)
564{
565    // only for root and write, all permission, but for appspawn must to check
566    // for clod start in new namespace, pid==1 and uid==root
567    if (srcLabel->cred.uid == 0 && srcLabel->cred.pid == 1 && mode == DAC_WRITE) {
568        return DAC_RESULT_PERMISSION;
569    }
570    int ret = 0;
571    if (srcLabel->cred.uid < PUBLIC_APP_BEGIN_UID) {
572        ret = DacCheckParamPermission(labelIndex, srcLabel, name, mode);
573    }
574#ifdef PARAM_SUPPORT_SELINUX
575    if (ret == DAC_RESULT_PERMISSION) {
576        ret = SelinuxCheckParamPermission(labelIndex, srcLabel, name, mode);
577    }
578#endif
579    return ret;
580}
581#endif
582
583STATIC_INLINE ParamTrieNode *BaseFindTrieNode(WorkSpace *workSpace,
584    const char *key, uint32_t keyLen, uint32_t *matchLabel)
585{
586    PARAM_CHECK(key != NULL && keyLen > 0, return NULL, "Invalid key ");
587    uint32_t tmpMatchLen = 0;
588    ParamTrieNode *node = FindTrieNode_(workSpace, key, keyLen, &tmpMatchLen);
589    if (matchLabel != NULL) {
590        *matchLabel = tmpMatchLen;
591    }
592    if (node != NULL && node->dataIndex != 0) {
593        ParamNode *entry = (ParamNode *)GetTrieNode(workSpace, node->dataIndex);
594        if (entry != NULL && entry->keyLength == keyLen) {
595            return node;
596        }
597        return NULL;
598    }
599    return node;
600}
601
602CachedHandle CachedParameterCreate(const char *name, const char *defValue)
603{
604    if (name == NULL || defValue == NULL) {
605        return NULL;
606    }
607    PARAM_WORKSPACE_CHECK(GetParamWorkSpace(), return NULL, "Invalid param workspace");
608    uint32_t nameLen = strlen(name);
609    PARAM_CHECK(nameLen < PARAM_NAME_LEN_MAX, return NULL, "Invalid name %s", name);
610    uint32_t valueLen = strlen(defValue);
611    if (IS_READY_ONLY(name)) {
612        PARAM_CHECK(valueLen < PARAM_CONST_VALUE_LEN_MAX, return NULL, "Illegal param value %s", defValue);
613    } else {
614        PARAM_CHECK(valueLen < PARAM_VALUE_LEN_MAX, return NULL, "Illegal param value %s length", defValue);
615    }
616
617    ParamTrieNode *node = NULL;
618    WorkSpace *workspace = NULL;
619    int ret = ReadParamWithCheck(&workspace, name, DAC_READ, &node);
620    PARAM_CHECK(ret == 0, return NULL, "Forbid to access parameter %s", name);
621    PARAM_CHECK(workspace != NULL && workspace->area != NULL, return NULL, "Forbid to access parameter %s", name);
622
623    CachedParameter *param = (CachedParameter *)malloc(
624        sizeof(CachedParameter) + PARAM_ALIGN(nameLen) + 1 + PARAM_VALUE_LEN_MAX);
625    PARAM_CHECK(param != NULL, return NULL, "Failed to create CachedParameter for %s", name);
626    ret = PARAM_STRCPY(param->data, nameLen + 1, name);
627    PARAM_CHECK(ret == 0, free(param);
628        return NULL, "Failed to copy name %s", name);
629    param->cachedParameterCheck = CachedParameterCheck;
630    param->workspace = workspace;
631    param->nameLen = nameLen;
632    param->paramValue = &param->data[PARAM_ALIGN(nameLen) + 1];
633    param->bufferLen = PARAM_VALUE_LEN_MAX;
634    param->dataCommitId = (uint32_t)-1;
635    if (node != NULL && node->dataIndex != 0) {
636        param->dataIndex = node->dataIndex;
637        ParamNode *entry = (ParamNode *)GetTrieNode(workspace, node->dataIndex);
638        PARAM_CHECK(entry != NULL, free(param);
639            return NULL, "Failed to get trie node %s", name);
640        uint32_t length = param->bufferLen;
641        param->dataCommitId = ReadCommitId(entry);
642        ret = ReadParamValue_(entry, &param->dataCommitId, param->paramValue, &length);
643        PARAM_CHECK(ret == 0, free(param);
644            return NULL, "Failed to read parameter value %s", name);
645    } else {
646        param->dataIndex = 0;
647        ret = PARAM_STRCPY(param->paramValue, param->bufferLen, defValue);
648        PARAM_CHECK(ret == 0, free(param);
649            return NULL, "Failed to copy name %s", name);
650    }
651    param->spaceCommitId = ATOMIC_UINT64_LOAD_EXPLICIT(&workspace->area->commitId, MEMORY_ORDER_ACQUIRE);
652    PARAM_LOGV("CachedParameterCreate %u %u %lld \n", param->dataIndex, param->dataCommitId, param->spaceCommitId);
653    return (CachedHandle)param;
654}
655
656STATIC_INLINE const char *CachedParameterCheck(CachedParameter *param, int *changed)
657{
658    *changed = 0;
659    if (param->dataIndex == 0) {
660        ParamTrieNode *node = BaseFindTrieNode(param->workspace, param->data, param->nameLen, NULL);
661        if (node != NULL) {
662            param->dataIndex = node->dataIndex;
663        } else {
664            return param->paramValue;
665        }
666    }
667    ParamNode *entry = (ParamNode *)GetTrieNode(param->workspace, param->dataIndex);
668    PARAM_CHECK(entry != NULL, return param->paramValue, "Failed to get trie node %s", param->data);
669    uint32_t dataCommitId = ATOMIC_LOAD_EXPLICIT(&entry->commitId, MEMORY_ORDER_ACQUIRE);
670    dataCommitId &= PARAM_FLAGS_COMMITID;
671    if (param->dataCommitId == dataCommitId) {
672        return param->paramValue;
673    }
674    uint32_t length = param->bufferLen;
675    param->dataCommitId = dataCommitId;
676    int ret = ReadParamValue_(entry, &param->dataCommitId, param->paramValue, &length);
677    PARAM_CHECK(ret == 0, return NULL, "Failed to copy value %s", param->data);
678    PARAM_LOGV("CachedParameterCheck %u", param->dataCommitId);
679    *changed = 1;
680    return param->paramValue;
681}
682
683void CachedParameterDestroy(CachedHandle handle)
684{
685    if (handle != NULL) {
686        free(handle);
687    }
688}
689