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#include <errno.h>
16#include <dlfcn.h>
17#include <sys/socket.h>
18
19#include "init_utils.h"
20#include "param_manager.h"
21#include "param_security.h"
22#include "param_utils.h"
23#include "param_base.h"
24#ifdef PARAM_SUPPORT_SELINUX
25#include "selinux_parameter.h"
26#endif
27
28#if defined (__aarch64__) || defined(__x86_64__) || (defined(__riscv) && __riscv_xlen == 64)
29#define CHECKER_LIB_NAME "/system/lib64/libparaperm_checker.z.so"
30#define CHECKER_UPDATER_LIB "/lib64/libparaperm_checker.z.so"
31#else
32#define CHECKER_LIB_NAME "/system/lib/libparaperm_checker.z.so"
33#define CHECKER_UPDATER_LIB "/lib/libparaperm_checker.z.so"
34#endif
35typedef int (*SelinuxSetParamCheck)(const char *paraName, const char *destContext, const SrcInfo *info);
36
37static int InitSelinuxOpsForInit(SelinuxSpace *selinuxSpace)
38{
39    if (selinuxSpace->selinuxHandle == NULL) {
40        const char *libname = (GetParamWorkSpace()->ops.updaterMode == 1) ? CHECKER_UPDATER_LIB : CHECKER_LIB_NAME;
41        selinuxSpace->selinuxHandle = dlopen(libname, RTLD_LAZY);
42        PARAM_CHECK(selinuxSpace->selinuxHandle != NULL,
43            return 0, "Failed to dlsym selinuxHandle, %s", dlerror());
44    }
45    void *handle = selinuxSpace->selinuxHandle;
46    if (selinuxSpace->setParamCheck == NULL) {
47        selinuxSpace->setParamCheck = (SelinuxSetParamCheck)dlsym(handle, "SetParamCheck");
48        PARAM_CHECK(selinuxSpace->setParamCheck != NULL, return -1, "Failed to dlsym setParamCheck %s", dlerror());
49    }
50    if (selinuxSpace->getParamList == NULL) {
51        selinuxSpace->getParamList = (ParamContextsList *(*)()) dlsym(handle, "GetParamList");
52        PARAM_CHECK(selinuxSpace->getParamList != NULL, return -1, "Failed to dlsym getParamList %s", dlerror());
53    }
54    if (selinuxSpace->getParamLabel == NULL) {
55        selinuxSpace->getParamLabel = (const char *(*)(const char *))dlsym(handle, "GetParamLabel");
56        PARAM_CHECK(selinuxSpace->getParamLabel != NULL, return -1, "Failed to dlsym getParamLabel %s", dlerror());
57    }
58    if (selinuxSpace->initParamSelinux == NULL) {
59        selinuxSpace->initParamSelinux = (int (*)(int))dlsym(handle, "InitParamSelinux");
60        PARAM_CHECK(selinuxSpace->initParamSelinux != NULL, return -1, "Failed to dlsym initParamSelinux ");
61    }
62    if (selinuxSpace->getParamLabelIndex == NULL) {
63        selinuxSpace->getParamLabelIndex = (int (*)(const char *))dlsym(handle, "GetParamLabelIndex");
64        PARAM_CHECK(selinuxSpace->getParamLabelIndex != NULL, return -1, "Failed to dlsym getParamLabelIndex ");
65    }
66    if (selinuxSpace->setSelinuxLogCallback == NULL) {
67        selinuxSpace->setSelinuxLogCallback = (void (*)())dlsym(handle, "SetInitSelinuxLog");
68    }
69    if (selinuxSpace->destroyParamList == NULL) {
70        selinuxSpace->destroyParamList =
71            (void (*)(ParamContextsList **))dlsym(handle, "DestroyParamList");
72        PARAM_CHECK(selinuxSpace->destroyParamList != NULL,
73            return -1, "Failed to dlsym destroyParamList %s", dlerror());
74    }
75
76    // init and open avc log
77    int ret = selinuxSpace->initParamSelinux(1);
78    if (selinuxSpace->setSelinuxLogCallback != NULL) {
79        selinuxSpace->setSelinuxLogCallback();
80    }
81    return ret;
82}
83
84static int InitLocalSecurityLabel(ParamSecurityLabel *security, int isInit)
85{
86    PARAM_CHECK(GetParamWorkSpace() != NULL, return -1, "Invalid workspace");
87    UNUSED(isInit);
88    PARAM_CHECK(security != NULL, return -1, "Invalid security");
89    security->cred.pid = getpid();
90    security->cred.uid = geteuid();
91    security->cred.gid = getegid();
92    security->flags[PARAM_SECURITY_SELINUX] = 0;
93    PARAM_LOGV("InitLocalSecurityLabel");
94#if !(defined STARTUP_INIT_TEST || defined LOCAL_TEST)
95    if ((bool)isInit) {
96        int ret = InitSelinuxOpsForInit(&GetParamWorkSpace()->selinuxSpace);
97        PARAM_CHECK(ret == 0, return -1, "Failed to init selinux ops");
98    } else {
99        SelinuxSpace *selinuxSpace = &GetParamWorkSpace()->selinuxSpace;
100        selinuxSpace->initParamSelinux = InitParamSelinux;
101        selinuxSpace->getParamList = GetParamList;
102        selinuxSpace->getParamLabel = GetParamLabel;
103        selinuxSpace->destroyParamList = DestroyParamList;
104        selinuxSpace->getParamLabelIndex = GetParamLabelIndex;
105        // init
106        selinuxSpace->initParamSelinux(isInit);
107    }
108#endif
109    PARAM_LOGV("Load selinux lib success.");
110    return 0;
111}
112
113static int FreeLocalSecurityLabel(ParamSecurityLabel *srcLabel)
114{
115    return 0;
116}
117
118static void SetSelinuxFileCon(const char *name, const char *context)
119{
120    PARAM_CHECK(GetParamWorkSpace() != NULL && GetParamWorkSpace()->ops.setfilecon != NULL,
121        return, "Invalid workspace or setfilecon");
122    static char buffer[FILENAME_LEN_MAX] = {0};
123    int len = PARAM_SPRINTF(buffer, sizeof(buffer), "%s/%s", PARAM_STORAGE_PATH, context);
124    if (len > 0) {
125        buffer[len] = '\0';
126        PARAM_LOGV("setfilecon name %s path: %s %s ", name, context, buffer);
127        if (GetParamWorkSpace()->ops.setfilecon(buffer, context) < 0) {
128            PARAM_LOGE("Failed to setfilecon %s ", context);
129        }
130    }
131}
132
133static void HandleSelinuxLabelForOpen(const ParameterNode *paramNode, int readOnly)
134{
135    uint32_t labelIndex = paramNode->index + WORKSPACE_INDEX_BASE;
136    int ret = OpenWorkSpace(labelIndex, readOnly);
137    if (ret != 0) {
138        PARAM_LOGE("Forbid to add selinux workspace %s %s", paramNode->paraName, paramNode->paraContext);
139        return;
140    }
141    if (!readOnly) {
142        // set selinux label
143        SetSelinuxFileCon(paramNode->paraName, paramNode->paraContext);
144    }
145}
146
147static void HandleSelinuxLabelForInit(const ParameterNode *paramNode, int readOnly)
148{
149    uint32_t labelIndex = paramNode->index + WORKSPACE_INDEX_BASE;
150    int ret = AddWorkSpace(paramNode->paraContext, labelIndex, readOnly, 0);
151    PARAM_CHECK(ret == 0, return, "Not enough memory for %s", paramNode->paraContext);
152
153    ParamWorkSpace *paramSpace = GetParamWorkSpace();
154    PARAM_CHECK(paramSpace != NULL, return, "Invalid workspace");
155    if (paramSpace->maxLabelIndex < labelIndex) {
156        paramSpace->maxLabelIndex = labelIndex;
157    }
158}
159
160int SelinuxGetAllLabel(int readOnly,
161    void (*handleSelinuxLabel)(const ParameterNode *paramNode, int readOnly))
162{
163    SelinuxSpace *selinuxSpace = &GetParamWorkSpace()->selinuxSpace;
164    PARAM_CHECK(selinuxSpace->getParamList != NULL, return DAC_RESULT_FORBIDED, "Invalid getParamList");
165    ParamContextsList *node = selinuxSpace->getParamList();
166    int count = 0;
167    while (node != NULL) {
168        PARAM_LOGV("SelinuxGetAllLabel index %d name %s content %s",
169            node->info.index, node->info.paraName, node->info.paraContext);
170        if (node->info.paraContext == NULL || node->info.paraName == NULL) {
171            node = node->next;
172            continue;
173        }
174        handleSelinuxLabel(&node->info, readOnly);
175        count++;
176        node = node->next;
177    }
178    ParameterNode tmpNode = {WORKSPACE_NAME_DEF_SELINUX, WORKSPACE_NAME_DEF_SELINUX, 0};
179    handleSelinuxLabel(&tmpNode, readOnly);
180    PARAM_LOGV("Selinux get all label counts %d.", count);
181    return 0;
182}
183
184static void HandleSelinuxLabelForPermission(const ParameterNode *paramNode, int readOnly)
185{
186    uint32_t labelIndex = paramNode->index + WORKSPACE_INDEX_BASE;
187    if (labelIndex == WORKSPACE_INDEX_BASE) {
188        return;
189    }
190    if (*(paramNode->paraName + strlen(paramNode->paraName) - 1) != '.') {
191        return;
192    }
193    // save selinux index
194    ParamWorkSpace *paramWorkspace = GetParamWorkSpace();
195    PARAM_CHECK(paramWorkspace != NULL && paramWorkspace->workSpace != NULL, return, "Invalid workspace");
196    WorkSpace *space = paramWorkspace->workSpace[WORKSPACE_INDEX_DAC];
197    PARAM_CHECK(space != NULL && space->area != NULL, return, "Failed to get dac space %s", paramNode->paraName);
198    uint32_t index = 0;
199    (void)FindTrieNode(space, paramNode->paraName, strlen(paramNode->paraName), &index);
200    ParamSecurityNode *node = (ParamSecurityNode *)GetTrieNode(space, index);
201    PARAM_CHECK(node != NULL, return, "Can not get security label for %s", paramNode->paraName);
202    PARAM_LOGV("HandleSelinuxLabelForPermission %s selinuxIndex [ %u %u] dac %u %s ",
203        paramNode->paraName, labelIndex, node->selinuxIndex, index, paramNode->paraContext);
204    ParamAuditData auditData = {0};
205    auditData.dacData.gid = node->gid;
206    auditData.dacData.uid = node->uid;
207    auditData.dacData.mode = node->mode;
208    auditData.dacData.paramType = node->type;
209    auditData.selinuxIndex = labelIndex;
210    auditData.name = paramNode->paraName;
211    auditData.memberNum = 1;
212    auditData.members[0] = node->gid;
213    AddSecurityLabel(&auditData);
214}
215
216static int SelinuxGetParamSecurityLabel(const char *cmd, int readOnly)
217{
218    if (cmd == NULL || strcmp(cmd, "create") == 0) { // for init and other processor
219        return SelinuxGetAllLabel(readOnly, HandleSelinuxLabelForInit);
220    }
221    if ((strcmp(cmd, "init") == 0) && (!readOnly)) { // only for init
222        return SelinuxGetAllLabel(readOnly, HandleSelinuxLabelForOpen);
223    }
224    if ((strcmp(cmd, "permission") == 0) && (!readOnly)) { // only for init
225        return SelinuxGetAllLabel(readOnly, HandleSelinuxLabelForPermission);
226    }
227    if ((strcmp(cmd, "open") == 0) && readOnly) { // for read only
228        static int loadLabels = 0;
229        if (loadLabels) {
230            return 0;
231        }
232        loadLabels = 1;
233        return SelinuxGetAllLabel(readOnly, HandleSelinuxLabelForOpen);
234    }
235    return 0;
236}
237
238static int SelinuxGetParamSecurityLabelForInit(const char *path)
239{
240    return SelinuxGetParamSecurityLabel(path, 0);
241}
242
243static int CheckFilePermission(const ParamSecurityLabel *localLabel, const char *fileName, int flags)
244{
245    UNUSED(flags);
246    UNUSED(localLabel);
247    UNUSED(fileName);
248    return 0;
249}
250
251static int UpdaterCheckParamPermission(const ParamLabelIndex *labelIndex,
252    const ParamSecurityLabel *srcLabel, const char *name, uint32_t mode)
253{
254    return DAC_RESULT_PERMISSION;
255}
256
257static int SelinuxGetParamSecurityLabelForOther(const char *path)
258{
259    return SelinuxGetParamSecurityLabel(path, 1);
260}
261
262INIT_LOCAL_API int RegisterSecuritySelinuxOps(ParamSecurityOps *ops, int isInit)
263{
264    PARAM_CHECK(GetParamWorkSpace() != NULL, return -1, "Invalid workspace");
265    PARAM_CHECK(ops != NULL, return -1, "Invalid param");
266    int ret = PARAM_STRCPY(ops->name, sizeof(ops->name), "selinux");
267    ops->securityGetLabel = NULL;
268    ops->securityInitLabel = InitLocalSecurityLabel;
269    ops->securityCheckFilePermission = CheckFilePermission;
270    if (GetParamWorkSpace()->ops.updaterMode == 1) {
271        ops->securityCheckParamPermission = UpdaterCheckParamPermission;
272    } else {
273#ifdef STARTUP_INIT_TEST
274        ops->securityCheckParamPermission = SelinuxCheckParamPermission;
275#endif
276    }
277    ops->securityFreeLabel = FreeLocalSecurityLabel;
278    if (isInit != 0) {
279        ops->securityGetLabel = SelinuxGetParamSecurityLabelForInit;
280    } else {
281        ops->securityGetLabel = SelinuxGetParamSecurityLabelForOther;
282    }
283    return ret;
284}
285