1/*
2 * Copyright (C) 2024 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 "appspawn_utils.h"
17
18#include <ctype.h>
19#include <dirent.h>
20#include <fcntl.h>
21#include <malloc.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <linux/if.h>
28#include <sys/ioctl.h>
29#include <sys/mount.h>
30#include <sys/socket.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33
34#include "appspawn_hook.h"
35#include "cJSON.h"
36#include "config_policy_utils.h"
37#include "json_utils.h"
38#include "parameter.h"
39#include "securec.h"
40
41static const AppSpawnCommonEnv COMMON_ENV[] = {
42    {"HNP_PRIVATE_HOME", "/data/app", true},
43    {"HNP_PUBLIC_HOME", "/data/service/hnp", true},
44    {"PATH", "${HNP_PRIVATE_HOME}/bin:${HNP_PUBLIC_HOME}/bin:${PATH}", true},
45    {"HOME", "/storage/Users/currentUser", false},
46    {"TMPDIR", "/data/storage/el2/base/cache", false},
47    {"SHELL", "/bin/sh", false},
48    {"PWD", "/storage/Users/currentUser", false}
49};
50
51int ConvertEnvValue(const char *srcEnv, char *dstEnv, int len)
52{
53    char *tmpEnv = NULL;
54    char *ptr;
55    char *tmpPtr1;
56    char *tmpPtr2;
57    char *envGet;
58
59    int srcLen = strlen(srcEnv) + 1;
60    tmpEnv = malloc(srcLen);
61    APPSPAWN_CHECK(tmpEnv != NULL, return -1, "malloc size=%{public}d fail", srcLen);
62
63    int ret = memcpy_s(tmpEnv, srcLen, srcEnv, srcLen);
64    if (ret != EOK) {
65        APPSPAWN_LOGE("Failed to copy env value");
66        free(tmpEnv);
67        return -1;
68    }
69
70    ptr = tmpEnv;
71    dstEnv[0] = 0;
72    while (((tmpPtr1 = strchr(ptr, '$')) != NULL) && (*(tmpPtr1 + 1) == '{') &&
73        ((tmpPtr2 = strchr(tmpPtr1, '}')) != NULL)) {
74        *tmpPtr1 = 0;
75        ret = strcat_s(dstEnv, len, ptr);
76        if (ret != 0) {
77            APPSPAWN_LOGE("Failed to strcat env value");
78            free(tmpEnv);
79            return -1;
80        }
81        *tmpPtr2 = 0;
82        tmpPtr1++;
83        ptr = tmpPtr2 + 1;
84        envGet = getenv(tmpPtr1 + 1);
85        if (envGet == NULL) {
86            continue;
87        }
88        ret = strcat_s(dstEnv, len, envGet);
89        if (ret != 0) {
90            APPSPAWN_LOGE("Failed to strcat env value");
91            free(tmpEnv);
92            return -1;
93        }
94    }
95    ret = strcat_s(dstEnv, len, ptr);
96    if (ret != 0) {
97        APPSPAWN_LOGE("Failed to strcat env value");
98        free(tmpEnv);
99        return -1;
100    }
101    free(tmpEnv);
102    return 0;
103}
104
105void InitCommonEnv(void)
106{
107    uint32_t count = ARRAY_LENGTH(COMMON_ENV);
108    int32_t ret;
109    char envValue[MAX_ENV_VALUE_LEN];
110    int developerMode = IsDeveloperModeOpen();
111
112    for (uint32_t i = 0; i < count; i++) {
113        if ((COMMON_ENV[i].developerModeEnable == true && developerMode == false)) {
114            continue;
115        }
116        ret = ConvertEnvValue(COMMON_ENV[i].envValue, envValue, MAX_ENV_VALUE_LEN);
117        APPSPAWN_CHECK(ret == 0, return, "Convert env value fail name=%{public}s, value=%{public}s",
118            COMMON_ENV[i].envName, COMMON_ENV[i].envValue);
119        ret = setenv(COMMON_ENV[i].envName, envValue, true);
120        APPSPAWN_CHECK(ret == 0, return, "Set env fail name=%{public}s, value=%{public}s",
121            COMMON_ENV[i].envName, envValue);
122    }
123}
124
125uint64_t DiffTime(const struct timespec *startTime, const struct timespec *endTime)
126{
127    APPSPAWN_CHECK_ONLY_EXPER(startTime != NULL, return 0);
128    APPSPAWN_CHECK_ONLY_EXPER(endTime != NULL, return 0);
129
130    uint64_t diff = (uint64_t)((endTime->tv_sec - startTime->tv_sec) * 1000000);  // 1000000 s-us
131    if (endTime->tv_nsec > startTime->tv_nsec) {
132        diff += (endTime->tv_nsec - startTime->tv_nsec) / 1000;  // 1000 ns - us
133    } else {
134        diff -= (startTime->tv_nsec - endTime->tv_nsec) / 1000;  // 1000 ns - us
135    }
136    return diff;
137}
138
139int MakeDirRec(const char *path, mode_t mode, int lastPath)
140{
141    APPSPAWN_CHECK(path != NULL && *path != '\0', return -1, "Invalid path to create");
142    char buffer[PATH_MAX] = {0};
143    const char slash = '/';
144    const char *p = path;
145    char *curPos = strchr(path, slash);
146    while (curPos != NULL) {
147        int len = curPos - p;
148        p = curPos + 1;
149        if (len == 0) {
150            curPos = strchr(p, slash);
151            continue;
152        }
153        int ret = memcpy_s(buffer, PATH_MAX, path, p - path - 1);
154        APPSPAWN_CHECK(ret == 0, return -1, "Failed to copy path");
155        ret = mkdir(buffer, mode);
156        if (ret == -1 && errno != EEXIST) {
157            return errno;
158        }
159        curPos = strchr(p, slash);
160    }
161    if (lastPath) {
162        if (mkdir(path, mode) == -1 && errno != EEXIST) {
163            return errno;
164        }
165    }
166    return 0;
167}
168
169static void TrimTail(char *buffer, uint32_t maxLen)
170{
171    uint32_t index = maxLen - 1;
172    while (index > 0) {
173        if (isspace(buffer[index])) {
174            buffer[index] = '\0';
175            index--;
176            continue;
177        }
178        break;
179    }
180}
181
182int32_t StringSplit(const char *str, const char *separator, void *context, SplitStringHandle handle)
183{
184    APPSPAWN_CHECK(str != NULL && handle != NULL && separator != NULL, return APPSPAWN_ARG_INVALID, "Invalid arg ");
185
186    int ret = 0;
187    char *tmp = (char *)str;
188    char buffer[PATH_MAX] = {0};
189    uint32_t len = strlen(separator);
190    uint32_t index = 0;
191    while ((*tmp != '\0') && (index < (uint32_t)sizeof(buffer))) {
192        if (index == 0 && isspace(*tmp)) {
193            tmp++;
194            continue;
195        }
196        if (strncmp(tmp, separator, len) != 0) {
197            buffer[index++] = *tmp;
198            tmp++;
199            continue;
200        }
201        tmp += len;
202        buffer[index] = '\0';
203        TrimTail(buffer, index);
204        index = 0;
205
206        int result = handle(buffer, context);
207        if (result != 0) {
208            ret = result;
209        }
210    }
211    if (index > 0) {
212        buffer[index] = '\0';
213        TrimTail(buffer, index);
214        index = 0;
215        int result = handle(buffer, context);
216        if (result != 0) {
217            ret = result;
218        }
219    }
220    return ret;
221}
222
223char *GetLastStr(const char *str, const char *dst)
224{
225    APPSPAWN_CHECK_ONLY_EXPER(str != NULL, return NULL);
226    APPSPAWN_CHECK_ONLY_EXPER(dst != NULL, return NULL);
227
228    char *end = (char *)str + strlen(str);
229    size_t len = strlen(dst);
230    while (end != str) {
231        if (strncmp(end, dst, len) == 0) {
232            return end;
233        }
234        end--;
235    }
236    return NULL;
237}
238
239char *ReadFile(const char *fileName)
240{
241    APPSPAWN_CHECK_ONLY_EXPER(fileName != NULL, return NULL);
242    char *buffer = NULL;
243    FILE *fd = NULL;
244    do {
245        struct stat fileStat;
246        if (stat(fileName, &fileStat) != 0 ||
247            fileStat.st_size <= 0 || fileStat.st_size > MAX_JSON_FILE_LEN) {
248            return NULL;
249        }
250        fd = fopen(fileName, "r");
251        APPSPAWN_CHECK(fd != NULL, break, "Failed to open file  %{public}s", fileName);
252
253        buffer = (char *)malloc((size_t)(fileStat.st_size + 1));
254        APPSPAWN_CHECK(buffer != NULL, break, "Failed to alloc mem %{public}s", fileName);
255
256        size_t ret = fread(buffer, fileStat.st_size, 1, fd);
257        APPSPAWN_CHECK(ret == 1, break, "Failed to read %{public}s to buffer", fileName);
258        buffer[fileStat.st_size] = '\0';
259        (void)fclose(fd);
260        return buffer;
261    } while (0);
262
263    if (fd != NULL) {
264        (void)fclose(fd);
265        fd = NULL;
266    }
267    if (buffer != NULL) {
268        free(buffer);
269    }
270    return NULL;
271}
272
273cJSON *GetJsonObjFromFile(const char *jsonPath)
274{
275    APPSPAWN_CHECK_ONLY_EXPER(jsonPath != NULL && *jsonPath != '\0', NULL);
276    char *buffer = ReadFile(jsonPath);
277    APPSPAWN_CHECK_ONLY_EXPER(buffer != NULL, NULL);
278    cJSON *json = cJSON_Parse(buffer);
279    free(buffer);
280    return json;
281}
282
283int ParseJsonConfig(const char *basePath, const char *fileName, ParseConfig parseConfig, ParseJsonContext *context)
284{
285    APPSPAWN_CHECK_ONLY_EXPER(basePath != NULL, return APPSPAWN_ARG_INVALID);
286    APPSPAWN_CHECK_ONLY_EXPER(fileName != NULL, return APPSPAWN_ARG_INVALID);
287    APPSPAWN_CHECK_ONLY_EXPER(parseConfig != NULL, return APPSPAWN_ARG_INVALID);
288
289    // load sandbox config
290    char path[PATH_MAX] = {};
291    CfgFiles *files = GetCfgFiles(basePath);
292    if (files == NULL) {
293        return APPSPAWN_SANDBOX_NONE;
294    }
295    int ret = 0;
296    for (int i = 0; i < MAX_CFG_POLICY_DIRS_CNT; ++i) {
297        if (files->paths[i] == NULL) {
298            continue;
299        }
300        int len = snprintf_s(path, sizeof(path), sizeof(path) - 1, "%s%s", files->paths[i], fileName);
301        APPSPAWN_CHECK(len > 0 && (size_t)len < sizeof(path), ret = APPSPAWN_SANDBOX_INVALID;
302            continue, "Failed to format sandbox config file name %{public}s %{public}s", files->paths[i], fileName);
303        cJSON *root = GetJsonObjFromFile(path);
304        APPSPAWN_CHECK(root != NULL, ret = APPSPAWN_SANDBOX_INVALID;
305            continue, "Failed to load app data sandbox config %{public}s", path);
306        int rc = parseConfig(root, context);
307        if (rc != 0) {
308            ret = rc;
309        }
310        cJSON_Delete(root);
311    }
312    FreeCfgFiles(files);
313    return ret;
314}
315
316void DumpCurrentDir(char *buffer, uint32_t bufferLen, const char *dirPath)
317{
318    APPSPAWN_CHECK_ONLY_EXPER(buffer != NULL, return);
319    APPSPAWN_CHECK_ONLY_EXPER(dirPath != NULL, return);
320    APPSPAWN_CHECK_ONLY_EXPER(bufferLen > 0, return);
321
322    DIR *pDir = opendir(dirPath);
323    APPSPAWN_CHECK(pDir != NULL, return, "Read dir :%{public}s failed.%{public}d", dirPath, errno);
324
325    struct dirent *dp;
326    while ((dp = readdir(pDir)) != NULL) {
327        if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
328            continue;
329        }
330        if (dp->d_type == DT_DIR) {
331            APPSPAWN_LOGW(" Current path %{public}s/%{public}s ", dirPath, dp->d_name);
332            int ret = snprintf_s(buffer, bufferLen, bufferLen - 1, "%s/%s", dirPath, dp->d_name);
333            APPSPAWN_CHECK(ret > 0, break, "Failed to snprintf_s errno: %{public}d", errno);
334            char *path = strdup(buffer);
335            DumpCurrentDir(buffer, bufferLen, path);
336            free(path);
337        }
338    }
339    closedir(pDir);
340    return;
341}
342
343static FILE *g_dumpToStream = NULL;
344void SetDumpToStream(FILE *stream)
345{
346    g_dumpToStream = stream;
347}
348
349#if defined(__clang__)
350#    pragma clang diagnostic push
351#    pragma clang diagnostic ignored "-Wvarargs"
352#elif defined(__GNUC__)
353#    pragma GCC diagnostic push
354#    pragma GCC diagnostic ignored "-Wvarargs"
355#elif defined(_MSC_VER)
356#    pragma warning(push)
357#endif
358
359void AppSpawnDump(const char *fmt, ...)
360{
361    if (g_dumpToStream == NULL) {
362        return;
363    }
364    APPSPAWN_CHECK_ONLY_EXPER(fmt != NULL, return);
365    char format[128] = {0};  // 128 max buffer for format
366    uint32_t size = strlen(fmt);
367    int curr = 0;
368    for (uint32_t index = 0; index < size; index++) {
369        if (curr >= (int)sizeof(format)) {  // invalid format
370            return;
371        }
372        if (fmt[index] != '%') {
373            format[curr++] = fmt[index];
374            continue;
375        }
376        if (strncmp(&fmt[index + 1], "{public}", strlen("{public}")) == 0) {
377            format[curr++] = fmt[index];
378            index += strlen("{public}");
379            continue;
380        }
381        if (strncmp(&fmt[index + 1], "{private}", strlen("{private}")) == 0) {
382            format[curr++] = fmt[index];
383            index += strlen("{private}");
384            continue;
385        }
386    }
387    va_list vargs;
388    va_start(vargs, format);
389    (void)vfprintf(g_dumpToStream, format, vargs);
390    va_end(vargs);
391    (void)fflush(g_dumpToStream);
392}
393
394int IsDeveloperModeOpen()
395{
396    char tmp[32] = {0};  // 32 max
397    int ret = GetParameter("const.security.developermode.state", "", tmp, sizeof(tmp));
398    APPSPAWN_LOGV("IsDeveloperModeOpen ret %{public}d result: %{public}s", ret, tmp);
399    int enabled = (ret > 0 && strcmp(tmp, "true") == 0);
400    return enabled;
401}
402
403#if defined(__clang__)
404#    pragma clang diagnostic pop
405#elif defined(__GNUC__)
406#    pragma GCC diagnostic pop
407#elif defined(_MSC_VER)
408#    pragma warning(pop)
409#endif
410
411uint32_t GetSpawnTimeout(uint32_t def)
412{
413    uint32_t value = def;
414    char data[32] = {};  // 32 length
415    int ret = GetParameter("persist.appspawn.reqMgr.timeout", "0", data, sizeof(data));
416    if (ret > 0 && strcmp(data, "0") != 0) {
417        errno = 0;
418        value = (uint32_t)atoi(data);
419        return (errno != 0) ? def : ((value < def) ? def : value);
420    }
421    return value;
422}
423
424int EnableNewNetNamespace(void)
425{
426    int fd = open(DEVICE_VIRTUAL_NET_IO_FLAGS, O_WRONLY);
427    APPSPAWN_CHECK(fd >= 0, return APPSPAWN_SYSTEM_ERROR, "Failed to open file errno %{public}d", errno);
428
429    int ret = write(fd, IFF_LOOPBACK_VALUE, IFF_LOOPBACK_SIZE);
430    if (ret < 0) {
431        APPSPAWN_LOGE("Failed to write to file errno %{public}d", errno);
432    } else {
433        APPSPAWN_LOGI("Successfully enabled new net namespace");
434    }
435
436    close(fd);
437    return (ret >= 0) ? 0 : APPSPAWN_SYSTEM_ERROR;
438}
439
440void EnableCache(void)
441{
442    APPSPAWN_LOGV("enable cache for app process");
443    // enable cache for app process
444    mallopt(M_OHOS_CONFIG, M_TCACHE_PERFORMANCE_MODE);
445    mallopt(M_OHOS_CONFIG, M_ENABLE_OPT_TCACHE);
446    mallopt(M_SET_THREAD_CACHE, M_THREAD_CACHE_ENABLE);
447    mallopt(M_DELAYED_FREE, M_DELAYED_FREE_ENABLE);
448}
449