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 <dirent.h>
17 #include <fcntl.h>
18 #include <sched.h>
19 #include <signal.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23
24 #include "appspawn_hook.h"
25 #include "appspawn_manager.h"
26 #include "appspawn_utils.h"
27 #include "securec.h"
28 #ifdef WITH_SELINUX
29 #include "selinux/selinux.h"
30 #endif
31
32 #define PID_NS_INIT_UID 100000 // reserved for pid_ns_init process, avoid app, render proc, etc.
33 #define PID_NS_INIT_GID 100000
34
35 typedef struct {
36 AppSpawnExtData extData;
37 int nsSelfPidFd; // ns pid fd of appspawn
38 int nsInitPidFd; // ns pid fd of pid_ns_init
39 } AppSpawnNamespace;
40
41 APPSPAWN_STATIC pid_t GetPidByName(const char *name);
AppSpawnExtDataCompareDataId(ListNode * node,void * data)42 static int AppSpawnExtDataCompareDataId(ListNode *node, void *data)
43 {
44 AppSpawnExtData *extData = (AppSpawnExtData *)ListEntry(node, AppSpawnExtData, node);
45 return extData->dataId - *(uint32_t *)data;
46 }
47
GetAppSpawnNamespace(const AppSpawnMgr * content)48 APPSPAWN_STATIC AppSpawnNamespace *GetAppSpawnNamespace(const AppSpawnMgr *content)
49 {
50 APPSPAWN_CHECK_ONLY_EXPER(content != NULL, return NULL);
51 uint32_t dataId = EXT_DATA_NAMESPACE;
52 ListNode *node = OH_ListFind(&content->extData, (void *)&dataId, AppSpawnExtDataCompareDataId);
53 if (node == NULL) {
54 return NULL;
55 }
56 return (AppSpawnNamespace *)ListEntry(node, AppSpawnNamespace, extData);
57 }
58
DeleteAppSpawnNamespace(AppSpawnNamespace * namespace)59 static void DeleteAppSpawnNamespace(AppSpawnNamespace *namespace)
60 {
61 APPSPAWN_CHECK_ONLY_EXPER(namespace != NULL, return);
62 APPSPAWN_LOGV("DeleteAppSpawnNamespace");
63 OH_ListRemove(&namespace->extData.node);
64 OH_ListInit(&namespace->extData.node);
65
66 if (namespace->nsInitPidFd > 0) {
67 close(namespace->nsInitPidFd);
68 namespace->nsInitPidFd = -1;
69 }
70 if (namespace->nsSelfPidFd > 0) {
71 close(namespace->nsSelfPidFd);
72 namespace->nsSelfPidFd = -1;
73 }
74 free(namespace);
75 }
76
FreeAppSpawnNamespace(struct TagAppSpawnExtData * data)77 static void FreeAppSpawnNamespace(struct TagAppSpawnExtData *data)
78 {
79 AppSpawnNamespace *namespace = ListEntry(data, AppSpawnNamespace, extData);
80 APPSPAWN_CHECK_ONLY_EXPER(namespace != NULL, return);
81 DeleteAppSpawnNamespace(namespace);
82 }
83
CreateAppSpawnNamespace(void)84 static AppSpawnNamespace *CreateAppSpawnNamespace(void)
85 {
86 APPSPAWN_LOGV("CreateAppSpawnNamespace");
87 AppSpawnNamespace *namespace = (AppSpawnNamespace *)calloc(1, sizeof(AppSpawnNamespace));
88 APPSPAWN_CHECK(namespace != NULL, return NULL, "Failed to create sandbox");
89 namespace->nsInitPidFd = -1;
90 namespace->nsSelfPidFd = -1;
91 // ext data init
92 OH_ListInit(&namespace->extData.node);
93 namespace->extData.dataId = EXT_DATA_NAMESPACE;
94 namespace->extData.freeNode = FreeAppSpawnNamespace;
95 namespace->extData.dumpNode = NULL;
96 return namespace;
97 }
98
GetPidByName(const char * name)99 APPSPAWN_STATIC pid_t GetPidByName(const char *name)
100 {
101 int pid = -1; // initial pid set to -1
102 DIR *dir = opendir("/proc");
103 if (dir == NULL) {
104 return -1;
105 }
106
107 struct dirent *entry;
108 while ((entry = readdir(dir)) != NULL) {
109 if (entry->d_type != DT_DIR) {
110 continue;
111 }
112 long pidNum = strtol(entry->d_name, NULL, 10); // pid will not exceed a 10-digit decimal number
113 if (pidNum <= 0) {
114 continue;
115 }
116
117 char path[32]; // path that contains the process name
118 if (snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%s/comm", entry->d_name) < 0) {
119 continue;
120 }
121 FILE *file = fopen(path, "r");
122 if (file == NULL) {
123 continue;
124 }
125 char buffer[32]; // read the process name
126 if (fgets(buffer, sizeof(buffer), file) == NULL) {
127 (void)fclose(file);
128 continue;
129 }
130 buffer[strcspn(buffer, "\n")] = 0;
131 if (strcmp(buffer, name) != 0) {
132 (void)fclose(file);
133 continue;
134 }
135
136 APPSPAWN_LOGI("get pid of %{public}s success", name);
137 pid = (int)pidNum;
138 (void)fclose(file);
139 break;
140 }
141
142 closedir(dir);
143 return pid;
144 }
145
NsInitFunc()146 APPSPAWN_STATIC int NsInitFunc()
147 {
148 setuid(PID_NS_INIT_UID);
149 setgid(PID_NS_INIT_GID);
150 #ifdef WITH_SELINUX
151 setcon("u:r:pid_ns_init:s0");
152 #endif
153 char *argv[] = {"/system/bin/pid_ns_init", NULL};
154 execve("/system/bin/pid_ns_init", argv, NULL);
155 #ifndef APPSPAWN_TEST
156 _exit(0);
157 #endif
158 return 0;
159 }
160
GetNsPidFd(pid_t pid)161 APPSPAWN_STATIC int GetNsPidFd(pid_t pid)
162 {
163 char nsPath[256]; // filepath of ns pid
164 int ret = snprintf_s(nsPath, sizeof(nsPath), sizeof(nsPath) - 1, "/proc/%d/ns/pid", pid);
165 if (ret < 0) {
166 APPSPAWN_LOGE("SetPidNamespace failed, snprintf_s error:%{public}s", strerror(errno));
167 return -1;
168 }
169 int nsFd = open(nsPath, O_RDONLY);
170 if (nsFd < 0) {
171 APPSPAWN_LOGE("open ns pid:%{public}d failed, err:%{public}s", pid, strerror(errno));
172 return -1;
173 }
174 return nsFd;
175 }
176
PreLoadEnablePidNs(AppSpawnMgr * content)177 APPSPAWN_STATIC int PreLoadEnablePidNs(AppSpawnMgr *content)
178 {
179 APPSPAWN_LOGI("Enable pid namespace flags: 0x%{public}x", content->content.sandboxNsFlags);
180 if (IsColdRunMode(content)) {
181 return 0;
182 }
183 if (IsNWebSpawnMode(content)) { // only for appspawn
184 return 0;
185 }
186 if (!(content->content.sandboxNsFlags & CLONE_NEWPID)) {
187 return 0;
188 }
189 AppSpawnNamespace *namespace = CreateAppSpawnNamespace();
190 APPSPAWN_CHECK(namespace != NULL, return -1, "Failed to create namespace");
191
192 int ret = -1;
193 // check if process pid_ns_init exists, this is the init process for pid namespace
194 pid_t pid = GetPidByName("pid_ns_init");
195 if (pid == -1) {
196 APPSPAWN_LOGI("Start Create pid_ns_init %{public}d", pid);
197 pid = clone(NsInitFunc, NULL, CLONE_NEWPID, NULL);
198 if (pid < 0) {
199 APPSPAWN_LOGE("clone pid ns init failed");
200 DeleteAppSpawnNamespace(namespace);
201 return ret;
202 }
203 } else {
204 APPSPAWN_LOGI("pid_ns_init exists, no need to create");
205 }
206
207 namespace->nsSelfPidFd = GetNsPidFd(getpid());
208 if (namespace->nsSelfPidFd < 0) {
209 APPSPAWN_LOGE("open ns pid of appspawn fail");
210 DeleteAppSpawnNamespace(namespace);
211 return ret;
212 }
213
214 namespace->nsInitPidFd = GetNsPidFd(pid);
215 if (namespace->nsInitPidFd < 0) {
216 APPSPAWN_LOGE("open ns pid of pid_ns_init fail");
217 DeleteAppSpawnNamespace(namespace);
218 return ret;
219 }
220 OH_ListAddTail(&content->extData, &namespace->extData.node);
221 APPSPAWN_LOGI("Enable pid namespace success.");
222 return 0;
223 }
224
225 // after calling setns, new process will be in the same pid namespace of the input pid
SetPidNamespace(int nsPidFd,int nsType)226 static int SetPidNamespace(int nsPidFd, int nsType)
227 {
228 APPSPAWN_LOGI("SetPidNamespace 0x%{public}x", nsType);
229 #ifndef APPSPAWN_TEST
230 if (setns(nsPidFd, nsType) < 0) {
231 APPSPAWN_LOGE("set pid namespace nsType:%{public}d failed", nsType);
232 return -1;
233 }
234 #endif
235 return 0;
236 }
237
PreForkSetPidNamespace(AppSpawnMgr * content,AppSpawningCtx * property)238 static int PreForkSetPidNamespace(AppSpawnMgr *content, AppSpawningCtx *property)
239 {
240 AppSpawnNamespace *namespace = GetAppSpawnNamespace(content);
241 if (namespace == NULL) {
242 return 0;
243 }
244 if (content->content.sandboxNsFlags & CLONE_NEWPID) {
245 SetPidNamespace(namespace->nsInitPidFd, CLONE_NEWPID); // pid_ns_init is the init process
246 }
247 return 0;
248 }
249
PostForkSetPidNamespace(AppSpawnMgr * content,AppSpawningCtx * property)250 static int PostForkSetPidNamespace(AppSpawnMgr *content, AppSpawningCtx *property)
251 {
252 AppSpawnNamespace *namespace = GetAppSpawnNamespace(content);
253 if (namespace == NULL) {
254 return 0;
255 }
256 if (content->content.sandboxNsFlags & CLONE_NEWPID) {
257 SetPidNamespace(namespace->nsSelfPidFd, 0); // go back to original pid namespace
258 }
259
260 return 0;
261 }
262
MODULE_CONSTRUCTOR(void)263 MODULE_CONSTRUCTOR(void)
264 {
265 AddPreloadHook(HOOK_PRIO_LOWEST, PreLoadEnablePidNs);
266 AddAppSpawnHook(STAGE_PARENT_PRE_FORK, HOOK_PRIO_LOWEST, PreForkSetPidNamespace);
267 AddAppSpawnHook(STAGE_PARENT_POST_FORK, HOOK_PRIO_HIGHEST, PostForkSetPidNamespace);
268 }
269