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 
GetUserIdByName(uid_t * uid,const char * name)39 static 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 
GetGroupIdByName(gid_t * gid,const char * name)49 static 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
GetParamDacData(ParamDacData * dacData,const char * value)67 static 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 
InitLocalSecurityLabel(ParamSecurityLabel * security,int isInit)111 static 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 
FreeLocalSecurityLabel(ParamSecurityLabel * srcLabel)123 static int FreeLocalSecurityLabel(ParamSecurityLabel *srcLabel)
124 {
125     return 0;
126 }
127 
DacGetGroupMember(gid_t gid,uid_t * member,uint32_t * memberSize)128 static 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 
LoadOneParam_(const uint32_t * context,const char * name,const char * value)161 static 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 
LoadParamLabels(const char * fileName)183 static 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 
ProcessParamFile(const char * fileName,void * context)222 static int ProcessParamFile(const char *fileName, void *context)
223 {
224     UNUSED(context);
225     return LoadParamLabels(fileName);
226 }
227 
DacGetParamSecurityLabel(const char * path)228 static 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 
CheckFilePermission(const ParamSecurityLabel * localLabel,const char * fileName,int flags)277 static 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 
RegisterSecurityDacOps(ParamSecurityOps * ops,int isInit)284 INIT_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