1/*
2 * Copyright (c) 2021-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 <errno.h>
16#include <grp.h>
17#include <pwd.h>
18#include <sys/stat.h>
19#include <dirent.h>
20#include <string.h>
21
22#include "param_manager.h"
23#include "param_security.h"
24#include "param_trie.h"
25#include "param_utils.h"
26#include "param_base.h"
27
28#define MAX_MEMBER_IN_GROUP  128
29#define MAX_BUF_SIZE  1024
30#define INVALID_MODE 0550
31#ifdef STARTUP_INIT_TEST
32#define GROUP_FILE_PATH STARTUP_INIT_UT_PATH "/etc/group"
33#else
34#define GROUP_FILE_PATH "/etc/group"
35#endif
36#define OCT_BASE 8
37#define INVALID_UID(uid) ((uid) == (uid_t)-1)
38
39static void GetUserIdByName(uid_t *uid, const char *name)
40{
41    struct passwd *data = getpwnam(name);
42    if (data == NULL) {
43        *uid = -1;
44        return ;
45    }
46    *uid = data->pw_uid;
47}
48
49static void GetGroupIdByName(gid_t *gid, const char *name)
50{
51    *gid = -1;
52    struct group *data = getgrnam(name);
53    if (data != NULL) {
54        *gid = data->gr_gid;
55        return;
56    }
57    while ((data = getgrent()) != NULL) {
58        if ((data->gr_name != NULL) && (strcmp(data->gr_name, name) == 0)) {
59            *gid = data->gr_gid;
60            break;
61        }
62    }
63    endgrent();
64}
65
66// user:group:r|w
67static int GetParamDacData(ParamDacData *dacData, const char *value)
68{
69    static const struct {
70        const char *name;
71        int value;
72    } paramTypes[] = {
73        { "int", PARAM_TYPE_INT },
74        { "string", PARAM_TYPE_STRING },
75        { "bool", PARAM_TYPE_BOOL },
76    };
77
78    char *groupName = strstr(value, ":");
79    if (groupName == NULL) {
80        return -1;
81    }
82    char *mode = strstr(groupName + 1, ":");
83    if (mode == NULL) {
84        return -1;
85    }
86
87    uint32_t nameLen = groupName - value;
88    char *name = (char *)value;
89    name[nameLen] = '\0';
90    GetUserIdByName(&dacData->uid, name);
91    nameLen = mode - groupName - 1;
92    name = (char *)groupName + 1;
93    name[nameLen] = '\0';
94    GetGroupIdByName(&dacData->gid, name);
95
96    dacData->paramType = PARAM_TYPE_STRING;
97    char *type = strstr(mode + 1, ":");
98    if (type != NULL) {
99        *type = '\0';
100        type++;
101        for (size_t i = 0; (type != NULL) && (i < ARRAY_LENGTH(paramTypes)); i++) {
102            if (strcmp(paramTypes[i].name, type) == 0) {
103                dacData->paramType = paramTypes[i].value;
104            }
105        }
106    }
107    dacData->mode = (uint16_t)strtol(mode + 1, NULL, OCT_BASE);
108    return 0;
109}
110
111static int InitLocalSecurityLabel(ParamSecurityLabel *security, int isInit)
112{
113    UNUSED(isInit);
114    PARAM_CHECK(security != NULL, return -1, "Invalid security");
115    security->cred.pid = getpid();
116    security->cred.uid = geteuid();
117    security->cred.gid = getegid();
118    // support check write permission in client
119    security->flags[PARAM_SECURITY_DAC] |= LABEL_CHECK_IN_ALL_PROCESS;
120    return 0;
121}
122
123static int FreeLocalSecurityLabel(ParamSecurityLabel *srcLabel)
124{
125    return 0;
126}
127
128static int DacGetGroupMember(gid_t gid, uid_t *member, uint32_t *memberSize)
129{
130    int32_t inputLen = (int32_t)*memberSize;
131    *memberSize = 0;
132    struct group *data = getgrgid(gid);
133    if (data == NULL || data->gr_mem == NULL) {
134        return 0;
135    }
136    int i = 0;
137    int memIndex = 0;
138    while (data->gr_mem[i]) {
139        uid_t uid;
140        GetUserIdByName(&uid, data->gr_mem[i]);
141        if (INVALID_UID(uid)) {
142            i++;
143            continue;
144        }
145        if ((memIndex + 1) > inputLen) {
146            PARAM_LOGE("Not enough memory for uid member %u", gid);
147            break;
148        }
149        member[memIndex++] = uid;
150        i++;
151    }
152    uid_t uid = 0;
153    GetUserIdByName(&uid, data->gr_name);
154    if (!INVALID_UID(uid) && ((memIndex + 1) < inputLen)) {
155        member[memIndex++] = uid;
156    }
157    *memberSize = memIndex;
158    return 0;
159}
160
161static int LoadOneParam_(const uint32_t *context, const char *name, const char *value)
162{
163    ParamAuditData *auditData = (ParamAuditData *)context;
164    auditData->dacData.gid = -1;
165    auditData->dacData.uid = -1;
166    auditData->name = name;
167    int ret = GetParamDacData(&auditData->dacData, value);
168    PARAM_CHECK(ret == 0, return -1, "Failed to get param info %d %s", ret, name);
169    if (INVALID_UID(auditData->dacData.gid) || INVALID_UID(auditData->dacData.uid)) {
170        PARAM_LOGW("Invalid dac for '%s' gid %d uid %d", name, auditData->dacData.gid, auditData->dacData.uid);
171    }
172    // get uid from group
173    auditData->memberNum = MAX_MEMBER_IN_GROUP;
174    ret = DacGetGroupMember(auditData->dacData.gid, auditData->members, &auditData->memberNum);
175    if (ret != 0) {
176        auditData->memberNum = 1;
177        auditData->members[0] = auditData->dacData.gid;
178    }
179
180    return AddSecurityLabel(auditData);
181}
182
183static int LoadParamLabels(const char *fileName)
184{
185    int result = 0;
186    ParamAuditData *auditData = (ParamAuditData *)calloc(1,
187        sizeof(ParamAuditData) + sizeof(uid_t) * MAX_MEMBER_IN_GROUP);
188    if (auditData == NULL) {
189        PARAM_LOGE("Failed to alloc memory %s", fileName);
190        return result;
191    }
192    uint32_t infoCount = 0;
193    FILE *fp = fopen(fileName, "r");
194    const uint32_t buffSize = PARAM_NAME_LEN_MAX + PARAM_CONST_VALUE_LEN_MAX + 10;  // 10 size
195    char *buff = (char *)calloc(1, buffSize);
196    while (fp != NULL && buff != NULL && fgets(buff, buffSize, fp) != NULL) {
197        buff[buffSize - 1] = '\0';
198        result = SplitParamString(buff, NULL, 0, LoadOneParam_, (const uint32_t *)auditData);
199        if (result != 0) {
200            PARAM_LOGE("Failed to split string %s fileName %s, result is:%d", buff, fileName, result);
201            break;
202        }
203        infoCount++;
204    }
205
206    if (result == 0) {
207        PARAM_LOGI("Load parameter label total %u success %s", infoCount, fileName);
208    }
209
210    if (fp != NULL) {
211        (void)fclose(fp);
212    }
213    if (buff != NULL) {
214        free(buff);
215    }
216    if (auditData != NULL) {
217        free(auditData);
218    }
219    return result;
220}
221
222static int ProcessParamFile(const char *fileName, void *context)
223{
224    UNUSED(context);
225    return LoadParamLabels(fileName);
226}
227
228static int DacGetParamSecurityLabel(const char *path)
229{
230    PARAM_CHECK(path != NULL, return -1, "Invalid param");
231    struct stat st;
232    if ((stat(path, &st) == 0) && !S_ISDIR(st.st_mode)) {
233        return ProcessParamFile(path, NULL);
234    }
235
236    PARAM_LOGV("DacGetParamSecurityLabel %s ", path);
237    DIR *pDir = opendir(path);
238    PARAM_CHECK(pDir != NULL, return -1, "Read dir :%s failed.%d", path, errno);
239    char *fileName = calloc(1, MAX_BUF_SIZE);
240    PARAM_CHECK(fileName != NULL, closedir(pDir);
241        return -1, "Failed to malloc for %s", path);
242
243    struct dirent *dp;
244    uint32_t count = 0;
245    while ((dp = readdir(pDir)) != NULL) {
246        if (dp->d_type == DT_DIR) {
247            continue;
248        }
249        char *tmp = strstr(dp->d_name, ".para.dac");
250        if (tmp == NULL) {
251            continue;
252        }
253        if (strcmp(tmp, ".para.dac") != 0) {
254            continue;
255        }
256        int ret = PARAM_SPRINTF(fileName, MAX_BUF_SIZE, "%s/%s", path, dp->d_name);
257        if (ret <= 0) {
258            PARAM_LOGE("Failed to get file name for %s", dp->d_name);
259            continue;
260        }
261        if ((stat(fileName, &st) == 0) && !S_ISDIR(st.st_mode)) {
262            count++;
263            ret = ProcessParamFile(fileName, NULL);
264            if (ret != 0) {
265                free(fileName);
266                closedir(pDir);
267                return ret;
268            };
269        }
270    }
271    PARAM_LOGV("Get parameter security label dac number is %d, from %s.", count, path);
272    free(fileName);
273    closedir(pDir);
274    return 0;
275}
276
277static int CheckFilePermission(const ParamSecurityLabel *localLabel, const char *fileName, int flags)
278{
279    UNUSED(flags);
280    PARAM_CHECK(localLabel != NULL && fileName != NULL, return -1, "Invalid param");
281    return 0;
282}
283
284INIT_LOCAL_API int RegisterSecurityDacOps(ParamSecurityOps *ops, int isInit)
285{
286    PARAM_CHECK(ops != NULL, return -1, "Invalid param");
287    PARAM_LOGV("RegisterSecurityDacOps %d", isInit);
288    int ret = PARAM_STRCPY(ops->name, sizeof(ops->name), "dac");
289    ops->securityInitLabel = InitLocalSecurityLabel;
290    ops->securityCheckFilePermission = CheckFilePermission;
291#ifdef STARTUP_INIT_TEST
292    ops->securityCheckParamPermission = DacCheckParamPermission;
293#endif
294    ops->securityFreeLabel = FreeLocalSecurityLabel;
295    if (isInit) {
296        ops->securityGetLabel = DacGetParamSecurityLabel;
297    }
298    return ret;
299}
300