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 <sys/mount.h>
17#include <sys/stat.h>
18#include <sys/syscall.h>
19#include <sys/types.h>
20
21#include "appspawn_msg.h"
22#include "appspawn_sandbox.h"
23#include "appspawn_utils.h"
24#include "json_utils.h"
25#include "securec.h"
26
27#define SANDBOX_GROUP_PATH "/data/storage/el2/group/"
28#define SANDBOX_INSTALL_PATH "/data/storage/el1/bundle/"
29#define SANDBOX_OVERLAY_PATH "/data/storage/overlay/"
30
31static inline bool CheckPath(const char *name)
32{
33    return name != NULL && strcmp(name, ".") != 0 && strcmp(name, "..") != 0 && strstr(name, "/") == NULL;
34}
35
36APPSPAWN_STATIC int MountAllHsp(const SandboxContext *context, const cJSON *hsps)
37{
38    APPSPAWN_CHECK(context != NULL && hsps != NULL, return -1, "Invalid context or hsps");
39
40    int ret = 0;
41    cJSON *bundles = cJSON_GetObjectItemCaseSensitive(hsps, "bundles");
42    cJSON *modules = cJSON_GetObjectItemCaseSensitive(hsps, "modules");
43    cJSON *versions = cJSON_GetObjectItemCaseSensitive(hsps, "versions");
44    APPSPAWN_CHECK(bundles != NULL && cJSON_IsArray(bundles), return -1, "MountAllHsp: invalid bundles");
45    APPSPAWN_CHECK(modules != NULL && cJSON_IsArray(modules), return -1, "MountAllHsp: invalid modules");
46    APPSPAWN_CHECK(versions != NULL && cJSON_IsArray(versions), return -1, "MountAllHsp: invalid versions");
47    int count = cJSON_GetArraySize(bundles);
48    APPSPAWN_CHECK(count == cJSON_GetArraySize(modules), return -1, "MountAllHsp: sizes are not same");
49    APPSPAWN_CHECK(count == cJSON_GetArraySize(versions), return -1, "MountAllHsp: sizes are not same");
50
51    APPSPAWN_LOGI("MountAllHsp app: %{public}s, count: %{public}d", context->bundleName, count);
52    for (int i = 0; i < count; i++) {
53        char *libBundleName = cJSON_GetStringValue(cJSON_GetArrayItem(bundles, i));
54        char *libModuleName = cJSON_GetStringValue(cJSON_GetArrayItem(modules, i));
55        char *libVersion = cJSON_GetStringValue(cJSON_GetArrayItem(versions, i));
56        APPSPAWN_CHECK(CheckPath(libBundleName) && CheckPath(libModuleName) && CheckPath(libVersion),
57            return -1, "MountAllHsp: path error");
58
59        // src path
60        int len = sprintf_s(context->buffer[0].buffer, context->buffer[0].bufferLen, "%s%s/%s/%s",
61            PHYSICAL_APP_INSTALL_PATH, libBundleName, libVersion, libModuleName);
62        APPSPAWN_CHECK(len > 0, return -1, "Failed to format install path");
63        // sandbox path
64        len = sprintf_s(context->buffer[1].buffer, context->buffer[1].bufferLen, "%s%s%s/%s",
65            context->rootPath, SANDBOX_INSTALL_PATH, libBundleName, libModuleName);
66        APPSPAWN_CHECK(len > 0, return -1, "Failed to format install path");
67
68        CreateSandboxDir(context->buffer[1].buffer, FILE_MODE);
69        MountArg mountArg = {
70            context->buffer[0].buffer, context->buffer[1].buffer, NULL, MS_REC | MS_BIND, NULL, MS_SLAVE
71        };
72        ret = SandboxMountPath(&mountArg);
73        APPSPAWN_CHECK(ret == 0, return ret, "mount library failed %{public}d", ret);
74    }
75    return ret;
76}
77
78static inline char *GetLastPath(const char *libPhysicalPath)
79{
80    char *tmp = GetLastStr(libPhysicalPath, "/");
81    return tmp + 1;
82}
83
84APPSPAWN_STATIC int MountAllGroup(const SandboxContext *context, const cJSON *groups)
85{
86    APPSPAWN_CHECK(context != NULL && groups != NULL, return -1, "Invalid context or group");
87    mode_t mountFlags = MS_REC | MS_BIND;
88    mode_t mountSharedFlag = MS_SLAVE;
89    if (CheckAppSpawnMsgFlag(context->message, TLV_MSG_FLAGS, APP_FLAGS_ISOLATED_SANDBOX)) {
90        APPSPAWN_LOGV("MountAllGroup falsg is isolated");
91        mountSharedFlag |= MS_REMOUNT | MS_NODEV | MS_RDONLY | MS_BIND;
92    }
93    int ret = 0;
94    cJSON *dataGroupIds = cJSON_GetObjectItemCaseSensitive(groups, "dataGroupId");
95    cJSON *gids = cJSON_GetObjectItemCaseSensitive(groups, "gid");
96    cJSON *dirs = cJSON_GetObjectItemCaseSensitive(groups, "dir");
97    APPSPAWN_CHECK(dataGroupIds != NULL && cJSON_IsArray(dataGroupIds),
98        return -1, "MountAllGroup: invalid dataGroupIds");
99    APPSPAWN_CHECK(gids != NULL && cJSON_IsArray(gids), return -1, "MountAllGroup: invalid gids");
100    APPSPAWN_CHECK(dirs != NULL && cJSON_IsArray(dirs), return -1, "MountAllGroup: invalid dirs");
101    int count = cJSON_GetArraySize(dataGroupIds);
102    APPSPAWN_CHECK(count == cJSON_GetArraySize(gids), return -1, "MountAllGroup: sizes are not same");
103    APPSPAWN_CHECK(count == cJSON_GetArraySize(dirs), return -1, "MountAllGroup: sizes are not same");
104
105    APPSPAWN_LOGI("MountAllGroup: app: %{public}s, count: %{public}d", context->bundleName, count);
106    for (int i = 0; i < count; i++) {
107        cJSON *dirJson = cJSON_GetArrayItem(dirs, i);
108        APPSPAWN_CHECK(dirJson != NULL && cJSON_IsString(dirJson), return -1, "MountAllGroup: invalid dirJson");
109        const char *libPhysicalPath = cJSON_GetStringValue(dirJson);
110        APPSPAWN_CHECK(!CheckPath(libPhysicalPath), return -1, "MountAllGroup: path error");
111
112        char *dataGroupUuid = GetLastPath(libPhysicalPath);
113        int len = sprintf_s(context->buffer[0].buffer, context->buffer[0].bufferLen, "%s%s%s",
114            context->rootPath, SANDBOX_GROUP_PATH, dataGroupUuid);
115        APPSPAWN_CHECK(len > 0, return -1, "Failed to format install path");
116        APPSPAWN_LOGV("MountAllGroup src: '%{public}s' =>'%{public}s'", libPhysicalPath, context->buffer[0].buffer);
117
118        CreateSandboxDir(context->buffer[0].buffer, FILE_MODE);
119        MountArg mountArg = {libPhysicalPath, context->buffer[0].buffer, NULL, mountFlags, NULL, mountSharedFlag};
120        ret = SandboxMountPath(&mountArg);
121        APPSPAWN_CHECK(ret == 0, return ret, "mount library failed %{public}d", ret);
122    }
123    return ret;
124}
125
126typedef struct {
127    const SandboxContext *sandboxContext;
128    uint32_t srcSetLen;
129    char *mountedSrcSet;
130} OverlayContext;
131
132static int SetOverlayAppPath(const char *hapPath, void *context)
133{
134    APPSPAWN_LOGV("SetOverlayAppPath '%{public}s'", hapPath);
135    OverlayContext *overlayContext = (OverlayContext *)context;
136    const SandboxContext *sandboxContext = overlayContext->sandboxContext;
137
138    // src path
139    char *tmp = GetLastStr(hapPath, "/");
140    if (tmp == NULL) {
141        return 0;
142    }
143    int ret = strncpy_s(sandboxContext->buffer[0].buffer,
144        sandboxContext->buffer[0].bufferLen, hapPath, tmp - (char *)hapPath);
145    APPSPAWN_CHECK(ret == 0, return ret, "mount library failed %{public}d", ret);
146
147    if (strstr(overlayContext->mountedSrcSet, sandboxContext->buffer[0].buffer) != NULL) {
148        APPSPAWN_LOGV("%{public}s have mounted before, no need to mount twice.", sandboxContext->buffer[0].buffer);
149        return 0;
150    }
151    ret = strcat_s(overlayContext->mountedSrcSet, overlayContext->srcSetLen, "|");
152    APPSPAWN_CHECK(ret == 0, return ret, "Fail to add src path to set %{public}s", "|");
153    ret = strcat_s(overlayContext->mountedSrcSet, overlayContext->srcSetLen, sandboxContext->buffer[0].buffer);
154    APPSPAWN_CHECK(ret == 0, return ret, "Fail to add src path to set %{public}s", sandboxContext->buffer[0].buffer);
155
156    // sandbox path
157    tmp = GetLastStr(sandboxContext->buffer[0].buffer, "/");
158    if (tmp == NULL) {
159        return 0;
160    }
161    int len = sprintf_s(sandboxContext->buffer[1].buffer, sandboxContext->buffer[1].bufferLen, "%s%s",
162        sandboxContext->rootPath, SANDBOX_OVERLAY_PATH);
163    APPSPAWN_CHECK(len > 0, return -1, "Failed to format install path");
164    ret = strcat_s(sandboxContext->buffer[1].buffer, sandboxContext->buffer[1].bufferLen - len, tmp + 1);
165    APPSPAWN_CHECK(ret == 0, return ret, "mount library failed %{public}d", ret);
166    APPSPAWN_LOGV("SetOverlayAppPath path: '%{public}s' => '%{public}s'",
167        sandboxContext->buffer[0].buffer, sandboxContext->buffer[1].buffer);
168
169    (void)MakeDirRec(sandboxContext->buffer[1].buffer, FILE_MODE, 1);
170    MountArg mountArg = {
171        sandboxContext->buffer[0].buffer, sandboxContext->buffer[1].buffer, NULL, MS_REC | MS_BIND, NULL, MS_SHARED
172    };
173    int retMount = SandboxMountPath(&mountArg);
174    if (retMount != 0) {
175        APPSPAWN_LOGE("Fail to mount overlay path, src is %{public}s.", hapPath);
176        ret = retMount;
177    }
178    return ret;
179}
180
181static int SetOverlayAppSandboxConfig(const SandboxContext *context, const char *overlayInfo)
182{
183    APPSPAWN_CHECK(context != NULL && overlayInfo != NULL, return -1, "Invalid context or overlayInfo");
184    OverlayContext overlayContext;
185    overlayContext.sandboxContext = context;
186    overlayContext.srcSetLen = strlen(overlayInfo);
187    overlayContext.mountedSrcSet = (char *)calloc(1, overlayContext.srcSetLen + 1);
188    APPSPAWN_CHECK(overlayContext.mountedSrcSet != NULL, return -1, "Failed to create mountedSrcSet");
189    *(overlayContext.mountedSrcSet + overlayContext.srcSetLen) = '\0';
190    int ret = StringSplit(overlayInfo, "|", (void *)&overlayContext, SetOverlayAppPath);
191    APPSPAWN_LOGV("overlayContext->mountedSrcSet: '%{public}s'", overlayContext.mountedSrcSet);
192    free(overlayContext.mountedSrcSet);
193    return ret;
194}
195
196static inline cJSON *GetJsonObjFromProperty(const SandboxContext *context, const char *name)
197{
198    uint32_t size = 0;
199    char *extInfo = (char *)(GetAppSpawnMsgExtInfo(context->message, name, &size));
200    if (size == 0 || extInfo == NULL) {
201        return NULL;
202    }
203    APPSPAWN_LOGV("Get json name %{public}s value %{public}s", name, extInfo);
204    cJSON *root = cJSON_Parse(extInfo);
205    APPSPAWN_CHECK(root != NULL, return NULL, "Invalid ext info %{public}s for %{public}s", extInfo, name);
206    return root;
207}
208
209static int ProcessHSPListConfig(const SandboxContext *context, const AppSpawnSandboxCfg *appSandBox, const char *name)
210{
211    cJSON *root = GetJsonObjFromProperty(context, name);
212    APPSPAWN_CHECK_ONLY_EXPER(root != NULL, return 0);
213    int ret = MountAllHsp(context, root);
214    cJSON_Delete(root);
215    return ret;
216}
217
218static int ProcessDataGroupConfig(const SandboxContext *context, const AppSpawnSandboxCfg *appSandBox, const char *name)
219{
220    cJSON *root = GetJsonObjFromProperty(context, name);
221    APPSPAWN_CHECK_ONLY_EXPER(root != NULL, return 0);
222    int ret = MountAllGroup(context, root);
223    cJSON_Delete(root);
224    return ret;
225}
226
227static int ProcessOverlayAppConfig(const SandboxContext *context,
228    const AppSpawnSandboxCfg *appSandBox, const char *name)
229{
230    uint32_t size = 0;
231    char *extInfo = (char *)GetAppSpawnMsgExtInfo(context->message, name, &size);
232    if (size == 0 || extInfo == NULL) {
233        return 0;
234    }
235    APPSPAWN_LOGV("ProcessOverlayAppConfig name %{public}s value %{public}s", name, extInfo);
236    return SetOverlayAppSandboxConfig(context, extInfo);
237}
238
239struct ListNode g_sandboxExpandCfgList = {&g_sandboxExpandCfgList, &g_sandboxExpandCfgList};
240static int AppSandboxExpandAppCfgCompareName(ListNode *node, void *data)
241{
242    AppSandboxExpandAppCfgNode *varNode = ListEntry(node, AppSandboxExpandAppCfgNode, node);
243    return strncmp((char *)data, varNode->name, strlen(varNode->name));
244}
245
246static int AppSandboxExpandAppCfgComparePrio(ListNode *node1, ListNode *node2)
247{
248    AppSandboxExpandAppCfgNode *varNode1 = ListEntry(node1, AppSandboxExpandAppCfgNode, node);
249    AppSandboxExpandAppCfgNode *varNode2 = ListEntry(node2, AppSandboxExpandAppCfgNode, node);
250    return varNode1->prio - varNode2->prio;
251}
252
253static const AppSandboxExpandAppCfgNode *GetAppSandboxExpandAppCfg(const char *name)
254{
255    ListNode *node = OH_ListFind(&g_sandboxExpandCfgList, (void *)name, AppSandboxExpandAppCfgCompareName);
256    if (node == NULL) {
257        return NULL;
258    }
259    return ListEntry(node, AppSandboxExpandAppCfgNode, node);
260}
261
262int RegisterExpandSandboxCfgHandler(const char *name, int prio, ProcessExpandSandboxCfg handleExpandCfg)
263{
264    APPSPAWN_CHECK_ONLY_EXPER(name != NULL && handleExpandCfg != NULL, return APPSPAWN_ARG_INVALID);
265    if (GetAppSandboxExpandAppCfg(name) != NULL) {
266        return APPSPAWN_NODE_EXIST;
267    }
268
269    size_t len = APPSPAWN_ALIGN(strlen(name) + 1);
270    AppSandboxExpandAppCfgNode *node = (AppSandboxExpandAppCfgNode *)(malloc(sizeof(AppSandboxExpandAppCfgNode) + len));
271    APPSPAWN_CHECK(node != NULL, return APPSPAWN_SYSTEM_ERROR, "Failed to create sandbox");
272    // ext data init
273    OH_ListInit(&node->node);
274    node->cfgHandle = handleExpandCfg;
275    node->prio = prio;
276    int ret = strcpy_s(node->name, len, name);
277    APPSPAWN_CHECK(ret == 0, free(node);
278        return -1, "Failed to copy name %{public}s", name);
279    OH_ListAddWithOrder(&g_sandboxExpandCfgList, &node->node, AppSandboxExpandAppCfgComparePrio);
280    return 0;
281}
282
283int ProcessExpandAppSandboxConfig(const SandboxContext *context, const AppSpawnSandboxCfg *appSandBox, const char *name)
284{
285    APPSPAWN_CHECK_ONLY_EXPER(context != NULL && appSandBox != NULL, return APPSPAWN_ARG_INVALID);
286    APPSPAWN_CHECK_ONLY_EXPER(name != NULL, return APPSPAWN_ARG_INVALID);
287    APPSPAWN_LOGV("ProcessExpandAppSandboxConfig %{public}s.", name);
288    const AppSandboxExpandAppCfgNode *node = GetAppSandboxExpandAppCfg(name);
289    if (node != NULL && node->cfgHandle != NULL) {
290        return node->cfgHandle(context, appSandBox, name);
291    }
292    return 0;
293}
294
295void AddDefaultExpandAppSandboxConfigHandle(void)
296{
297    RegisterExpandSandboxCfgHandler("HspList", 0, ProcessHSPListConfig);
298    RegisterExpandSandboxCfgHandler("DataGroup", 1, ProcessDataGroupConfig);
299    RegisterExpandSandboxCfgHandler("Overlay", 2, ProcessOverlayAppConfig);  // 2 priority
300}
301
302void ClearExpandAppSandboxConfigHandle(void)
303{
304    OH_ListRemoveAll(&g_sandboxExpandCfgList, NULL);
305}
306