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 35typedef 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 41APPSPAWN_STATIC pid_t GetPidByName(const char *name); 42static int AppSpawnExtDataCompareDataId(ListNode *node, void *data) 43{ 44 AppSpawnExtData *extData = (AppSpawnExtData *)ListEntry(node, AppSpawnExtData, node); 45 return extData->dataId - *(uint32_t *)data; 46} 47 48APPSPAWN_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 59APPSPAWN_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 77APPSPAWN_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 84APPSPAWN_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 99APPSPAWN_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 146APPSPAWN_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 161APPSPAWN_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 177APPSPAWN_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 226static 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 238APPSPAWN_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 250APPSPAWN_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 263MODULE_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