• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 TagAppSpawnNamespace {
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 APPSPAWN_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 APPSPAWN_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 APPSPAWN_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 APPSPAWN_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         size_t newline_pos = strcspn(buffer, "\n");
131         APPSPAWN_CHECK_ONLY_EXPER(newline_pos >= sizeof(buffer), buffer[newline_pos] = 0);
132         if (strcmp(buffer, name) != 0) {
133             (void)fclose(file);
134             continue;
135         }
136 
137         APPSPAWN_LOGI("get pid of %{public}s success", name);
138         pid = (int)pidNum;
139         (void)fclose(file);
140         break;
141     }
142 
143     closedir(dir);
144     return pid;
145 }
146 
NsInitFunc()147 APPSPAWN_STATIC int NsInitFunc()
148 {
149     setuid(PID_NS_INIT_UID);
150     setgid(PID_NS_INIT_GID);
151 #ifdef WITH_SELINUX
152     setcon("u:r:pid_ns_init:s0");
153 #endif
154     char *argv[] = {"/system/bin/pid_ns_init", NULL};
155     execve("/system/bin/pid_ns_init", argv, NULL);
156 #ifndef APPSPAWN_TEST
157     _exit(0);
158 #endif
159     return 0;
160 }
161 
GetNsPidFd(pid_t pid)162 APPSPAWN_STATIC int GetNsPidFd(pid_t pid)
163 {
164     char nsPath[256];  // filepath of ns pid
165     int ret = snprintf_s(nsPath, sizeof(nsPath), sizeof(nsPath) - 1, "/proc/%d/ns/pid", pid);
166     if (ret < 0) {
167         APPSPAWN_LOGE("SetPidNamespace failed, snprintf_s error:%{public}s", strerror(errno));
168         return -1;
169     }
170     int nsFd = open(nsPath, O_RDONLY);
171     if (nsFd < 0) {
172         APPSPAWN_LOGE("open ns pid:%{public}d failed, err:%{public}s", pid, strerror(errno));
173         return -1;
174     }
175     return nsFd;
176 }
177 
PreLoadEnablePidNs(AppSpawnMgr * content)178 APPSPAWN_STATIC int PreLoadEnablePidNs(AppSpawnMgr *content)
179 {
180     APPSPAWN_LOGI("Enable pid namespace flags: 0x%{public}x", content->content.sandboxNsFlags);
181     if (IsColdRunMode(content)) {
182         return 0;
183     }
184     if (IsNWebSpawnMode(content)) {  // only for appspawn
185         return 0;
186     }
187     if (!(content->content.sandboxNsFlags & CLONE_NEWPID)) {
188         return 0;
189     }
190     AppSpawnNamespace *namespace = CreateAppSpawnNamespace();
191     APPSPAWN_CHECK(namespace != NULL, return -1, "Failed to create namespace");
192 
193     int ret = -1;
194     // check if process pid_ns_init exists, this is the init process for pid namespace
195     pid_t pid = GetPidByName("pid_ns_init");
196     if (pid == -1) {
197         APPSPAWN_LOGI("Start Create pid_ns_init %{public}d", pid);
198         pid = clone(NsInitFunc, NULL, CLONE_NEWPID, NULL);
199         if (pid < 0) {
200             APPSPAWN_LOGE("clone pid ns init failed");
201             DeleteAppSpawnNamespace(namespace);
202             return ret;
203         }
204     } else {
205         APPSPAWN_LOGI("pid_ns_init exists, no need to create");
206     }
207 
208     namespace->nsSelfPidFd = GetNsPidFd(getpid());
209     if (namespace->nsSelfPidFd < 0) {
210         APPSPAWN_LOGE("open ns pid of appspawn fail");
211         DeleteAppSpawnNamespace(namespace);
212         return ret;
213     }
214 
215     namespace->nsInitPidFd = GetNsPidFd(pid);
216     if (namespace->nsInitPidFd < 0) {
217         APPSPAWN_LOGE("open ns pid of pid_ns_init fail");
218         DeleteAppSpawnNamespace(namespace);
219         return ret;
220     }
221     OH_ListAddTail(&content->extData, &namespace->extData.node);
222     APPSPAWN_LOGI("Enable pid namespace success.");
223     return 0;
224 }
225 
226 // after calling setns, new process will be in the same pid namespace of the input pid
SetPidNamespace(int nsPidFd,int nsType)227 APPSPAWN_STATIC int SetPidNamespace(int nsPidFd, int nsType)
228 {
229     APPSPAWN_LOGI("SetPidNamespace 0x%{public}x", nsType);
230 #ifndef APPSPAWN_TEST
231     if (setns(nsPidFd, nsType) < 0) {
232         APPSPAWN_LOGE("set pid namespace nsType:%{public}d failed", nsType);
233         return -1;
234     }
235 #endif
236     return 0;
237 }
238 
PreForkSetPidNamespace(AppSpawnMgr * content,AppSpawningCtx * property)239 APPSPAWN_STATIC int PreForkSetPidNamespace(AppSpawnMgr *content, AppSpawningCtx *property)
240 {
241     AppSpawnNamespace *namespace = GetAppSpawnNamespace(content);
242     if (namespace == NULL) {
243         return 0;
244     }
245     if (content->content.sandboxNsFlags & CLONE_NEWPID) {
246         SetPidNamespace(namespace->nsInitPidFd, CLONE_NEWPID);  // pid_ns_init is the init process
247     }
248     return 0;
249 }
250 
PostForkSetPidNamespace(AppSpawnMgr * content,AppSpawningCtx * property)251 APPSPAWN_STATIC int PostForkSetPidNamespace(AppSpawnMgr *content, AppSpawningCtx *property)
252 {
253     AppSpawnNamespace *namespace = GetAppSpawnNamespace(content);
254     if (namespace == NULL) {
255         return 0;
256     }
257     if (content->content.sandboxNsFlags & CLONE_NEWPID) {
258         SetPidNamespace(namespace->nsSelfPidFd, 0);  // go back to original pid namespace
259     }
260 
261     return 0;
262 }
263 
MODULE_CONSTRUCTOR(void)264 MODULE_CONSTRUCTOR(void)
265 {
266     AddPreloadHook(HOOK_PRIO_LOWEST, PreLoadEnablePidNs);
267     AddAppSpawnHook(STAGE_PARENT_PRE_FORK, HOOK_PRIO_LOWEST, PreForkSetPidNamespace);
268     AddAppSpawnHook(STAGE_PARENT_POST_FORK, HOOK_PRIO_HIGHEST, PostForkSetPidNamespace);
269 }
270