/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef _GNU_SOURCE #define _GNU_SOURCE #include #include #include #include "appspawn_adapter.h" #include "appspawn_hook.h" #include "appspawn_service.h" #include "appspawn_msg.h" #include "appspawn_manager.h" #include "appspawn_silk.h" #include "init_param.h" #include "parameter.h" #include "securec.h" #ifdef CODE_SIGNATURE_ENABLE // for xpm #include "code_sign_attr_utils.h" #endif #ifdef SECURITY_COMPONENT_ENABLE #include "sec_comp_enhance_kit_c.h" #endif #ifdef WITH_SELINUX #include "selinux/selinux.h" #endif #define PROVISION_TYPE_DEBUG "debug" #define DEVICE_NULL_STR "/dev/null" #define PROCESS_START_TIME_ENV "PROCESS_START_TIME" #define BITLEN32 32 #define PID_NS_INIT_UID 100000 // reserved for pid_ns_init process, avoid app, render proc, etc. #define PID_NS_INIT_GID 100000 static int SetProcessName(const AppSpawnMgr *content, const AppSpawningCtx *property) { const char *processName = GetProcessName(property); APPSPAWN_CHECK(processName != NULL, return -EINVAL, "Can not get process name"); // 解析时已经检查 size_t len = strlen(processName); char shortName[MAX_LEN_SHORT_NAME] = {0}; // process short name max length 16 bytes. size_t copyLen = len; const char *pos = processName; if (len >= MAX_LEN_SHORT_NAME) { copyLen = MAX_LEN_SHORT_NAME - 1; pos += (len - copyLen); } bool isRet = strncpy_s(shortName, MAX_LEN_SHORT_NAME, pos, copyLen) != EOK; APPSPAWN_CHECK(!isRet, return EINVAL, "strncpy_s short name error: %{public}d", errno); // set short name isRet = prctl(PR_SET_NAME, shortName) == -1; APPSPAWN_CHECK(!isRet, return errno, "prctl(PR_SET_NAME) error: %{public}d", errno); // reset longProcName isRet = memset_s(content->content.longProcName, (size_t)content->content.longProcNameLen, 0, (size_t)content->content.longProcNameLen) != EOK; APPSPAWN_CHECK(!isRet, return EINVAL, "Failed to memset long process name"); // set long process name isRet = strncpy_s(content->content.longProcName, content->content.longProcNameLen, processName, len) != EOK; APPSPAWN_CHECK(!isRet, return EINVAL, "strncpy_s long name error: %{public}d longProcNameLen %{public}u", errno, content->content.longProcNameLen); return 0; } static int SetKeepCapabilities(const AppSpawnMgr *content, const AppSpawningCtx *property) { AppSpawnMsgDacInfo *dacInfo = (AppSpawnMsgDacInfo *)GetAppProperty(property, TLV_DAC_INFO); APPSPAWN_CHECK(dacInfo != NULL, return APPSPAWN_TLV_NONE, "No tlv %{public}d in msg %{public}s", TLV_DOMAIN_INFO, GetProcessName(property)); // set keep capabilities when user not root. if (dacInfo->uid != 0) { bool isRet = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1; APPSPAWN_CHECK(!isRet, return errno, "set keepcaps failed: %{public}d", errno); } return 0; } static int SetCapabilities(const AppSpawnMgr *content, const AppSpawningCtx *property) { // init cap struct __user_cap_header_struct capHeader; bool isRet = memset_s(&capHeader, sizeof(capHeader), 0, sizeof(capHeader)) != EOK; APPSPAWN_CHECK(!isRet, return -EINVAL, "Failed to memset cap header"); capHeader.version = _LINUX_CAPABILITY_VERSION_3; capHeader.pid = 0; struct __user_cap_data_struct capData[2]; // 2 is data number isRet = memset_s(&capData, sizeof(capData), 0, sizeof(capData)) != EOK; APPSPAWN_CHECK(!isRet, return -EINVAL, "Failed to memset cap data"); // init inheritable permitted effective zero #ifdef GRAPHIC_PERMISSION_CHECK const uint64_t inheriTable = 0; const uint64_t permitted = 0; const uint64_t effective = 0; #else const uint64_t inheriTable = 0x3fffffffff; const uint64_t permitted = 0x3fffffffff; const uint64_t effective = 0x3fffffffff; #endif capData[0].inheritable = (__u32)(inheriTable); capData[1].inheritable = (__u32)(inheriTable >> BITLEN32); capData[0].permitted = (__u32)(permitted); capData[1].permitted = (__u32)(permitted >> BITLEN32); capData[0].effective = (__u32)(effective); capData[1].effective = (__u32)(effective >> BITLEN32); // set capabilities isRet = capset(&capHeader, &capData[0]) != 0; APPSPAWN_CHECK(!isRet, return -errno, "Failed to capset errno: %{public}d", errno); return 0; } static void InitDebugParams(const AppSpawnMgr *content, const AppSpawningCtx *property) { #if defined(__aarch64__) || defined(__x86_64__) const char *debugSoPath = "/system/lib64/libhidebug.so"; #else const char *debugSoPath = "/system/lib/libhidebug.so"; #endif const char *processName = GetProcessName(property); APPSPAWN_CHECK(processName != NULL, return, "Can not get process name"); bool isRet = access(debugSoPath, F_OK) != 0; APPSPAWN_CHECK(!isRet, return, "access failed, errno: %{public}d debugSoPath: %{public}s", errno, debugSoPath); void *handle = dlopen(debugSoPath, RTLD_LAZY); APPSPAWN_CHECK(handle != NULL, return, "Failed to dlopen libhidebug.so errno: %{public}s", dlerror()); bool (*initParam)(const char *name); initParam = (bool (*)(const char *name))dlsym(handle, "InitEnvironmentParam"); APPSPAWN_CHECK(initParam != NULL, dlclose(handle); return, "Failed to dlsym errno: %{public}s", dlerror()); (*initParam)(processName); dlclose(handle); } static void ClearEnvironment(const AppSpawnMgr *content, const AppSpawningCtx *property) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGTERM); sigprocmask(SIG_UNBLOCK, &mask, NULL); // close child fd AppSpawningCtx *ctx = (AppSpawningCtx *)property; close(ctx->forkCtx.fd[0]); ctx->forkCtx.fd[0] = -1; return; } static int SetXpmConfig(const AppSpawnMgr *content, const AppSpawningCtx *property) { #ifdef CODE_SIGNATURE_ENABLE // nwebspawn no permission set xpm config if (IsNWebSpawnMode(content)) { return 0; } uint32_t len = 0; char *provisionType = GetAppPropertyExt(property, MSG_EXT_NAME_PROVISION_TYPE, &len); if (provisionType == NULL || len == 0) { APPSPAWN_LOGE("get provision type failed, defaut is %{public}s", PROVISION_TYPE_DEBUG); provisionType = PROVISION_TYPE_DEBUG; } AppSpawnMsgOwnerId *ownerInfo = (AppSpawnMsgOwnerId *)GetAppProperty(property, TLV_OWNER_INFO); int jitfortEnable = IsJitFortModeOn(property) ? 1 : 0; int idType = PROCESS_OWNERID_UNINIT; const char *ownerId = NULL; if (strcmp(provisionType, PROVISION_TYPE_DEBUG) == 0) { idType = PROCESS_OWNERID_DEBUG; } else if (ownerInfo == NULL) { idType = PROCESS_OWNERID_COMPAT; } else if (CheckAppMsgFlagsSet(property, APP_FLAGS_TEMP_JIT)) { idType = PROCESS_OWNERID_APP_TEMP_ALLOW; ownerId = ownerInfo->ownerId; } else { idType = PROCESS_OWNERID_APP; ownerId = ownerInfo->ownerId; } int ret = InitXpm(jitfortEnable, idType, ownerId); APPSPAWN_CHECK(ret == 0, return ret, "set xpm region failed: %{public}d", ret); #endif return 0; } static int SetUidGid(const AppSpawnMgr *content, const AppSpawningCtx *property) { if (IsAppSpawnMode(content)) { struct sched_param param = { 0 }; param.sched_priority = 0; int ret = sched_setscheduler(0, SCHED_OTHER, ¶m); APPSPAWN_CHECK_ONLY_LOG(ret == 0, "UpdateSchedPrio failed ret: %{public}d, %{public}d", ret, errno); } AppSpawnMsgDacInfo *dacInfo = (AppSpawnMsgDacInfo *)GetAppProperty(property, TLV_DAC_INFO); APPSPAWN_CHECK(dacInfo != NULL, return APPSPAWN_TLV_NONE, "No tlv %{public}d in msg %{public}s", TLV_DAC_INFO, GetProcessName(property)); // set gids int ret = setgroups(dacInfo->gidCount, (const gid_t *)(&dacInfo->gidTable[0])); APPSPAWN_CHECK(ret == 0, return errno, "setgroups failed: %{public}d, gids.size=%{public}u", errno, dacInfo->gidCount); // set gid if (IsNWebSpawnMode(content)) { gid_t gid = dacInfo->gid / UID_BASE; if (gid >= 100) { // 100 APPSPAWN_LOGE("SetUidGid invalid uid for nwebspawn %{public}d", dacInfo->gid); return 0; } ret = setresgid(dacInfo->gid, dacInfo->gid, dacInfo->gid); } else { ret = setresgid(dacInfo->gid, dacInfo->gid, dacInfo->gid); } APPSPAWN_CHECK(ret == 0, return errno, "setgid(%{public}u) failed: %{public}d", dacInfo->gid, errno); ret = SetSeccompFilter(content, property); APPSPAWN_CHECK(ret == 0, return ret, "Failed to set setSeccompFilter"); /* If the effective user ID is changed from 0 to nonzero, * then all capabilities are cleared from the effective set */ ret = setresuid(dacInfo->uid, dacInfo->uid, dacInfo->uid); APPSPAWN_CHECK(ret == 0, return errno, "setuid(%{public}u) failed: %{public}d", dacInfo->uid, errno); if (CheckAppMsgFlagsSet(property, APP_FLAGS_DEBUGGABLE) && IsDeveloperModeOn(property)) { setenv("HAP_DEBUGGABLE", "true", 1); if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) { APPSPAWN_LOGE("Failed to set app dumpable: %{public}s", strerror(errno)); } } else { setenv("HAP_DEBUGGABLE", "false", 1); } return 0; } static int32_t SetFileDescriptors(const AppSpawnMgr *content, const AppSpawningCtx *property) { #ifndef APPSPAWN_TEST // redirect stdin stdout stderr to /dev/null int devNullFd = open(DEVICE_NULL_STR, O_RDWR); if (devNullFd == -1) { APPSPAWN_LOGE("open dev_null error: %{public}d", errno); return (-errno); } // stdin if (dup2(devNullFd, STDIN_FILENO) == -1) { APPSPAWN_LOGE("dup2 STDIN error: %{public}d", errno); return (-errno); } // stdout if (dup2(devNullFd, STDOUT_FILENO) == -1) { APPSPAWN_LOGE("dup2 STDOUT error: %{public}d", errno); return (-errno); } // stderr if (dup2(devNullFd, STDERR_FILENO) == -1) { APPSPAWN_LOGE("dup2 STDERR error: %{public}d", errno); return (-errno); } if (devNullFd > STDERR_FILENO) { close(devNullFd); } #endif return 0; } static int32_t CheckTraceStatus(void) { int fd = open("/proc/self/status", O_RDONLY); APPSPAWN_CHECK(fd >= 0, return errno, "Failed to open /proc/self/status error: %{public}d", errno); char data[1024] = {0}; // 1024 is data length ssize_t dataNum = read(fd, data, sizeof(data) - 1); (void)close(fd); APPSPAWN_CHECK(dataNum > 0, return -1, "Failed to read file /proc/self/status error: %{public}d", errno); const char *tracerPid = "TracerPid:\t"; data[dataNum] = '\0'; char *traceStr = strstr(data, tracerPid); APPSPAWN_CHECK(traceStr != NULL, return -1, "Not found %{public}s", tracerPid); char *separator = strchr(traceStr, '\n'); APPSPAWN_CHECK(separator != NULL, return -1, "Not found %{public}s", "\n"); int len = separator - traceStr - strlen(tracerPid); char pid = *(traceStr + strlen(tracerPid)); if (len > 1 || pid != '0') { return 0; } return -1; } static int32_t WaitForDebugger(const AppSpawningCtx *property) { // wait for debugger only debugging is required and process is debuggable if (CheckAppMsgFlagsSet(property, APP_FLAGS_NATIVEDEBUG) && CheckAppMsgFlagsSet(property, APP_FLAGS_DEBUGGABLE)) { uint32_t count = 0; while (CheckTraceStatus() != 0) { #ifndef APPSPAWN_TEST usleep(1000 * 100); // sleep 1000 * 100 microsecond #else if (count > 0) { break; } #endif count++; // remind users to connect to the debugger every 60 * 10 times if (count % (10 * 60) == 0) { count = 0; APPSPAWN_LOGI("wait for debugger, please attach the process"); } } } return 0; } static int SpawnInitSpawningEnv(AppSpawnMgr *content, AppSpawningCtx *property) { APPSPAWN_LOGV("Spawning: clear env"); int ret = SetProcessName(content, property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); // close socket id and signal for child ClearEnvironment(content, property); ResetParamSecurityLabel(); ret = SetAppAccessToken(content, property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); return 0; } static int SpawnSetAppEnv(AppSpawnMgr *content, AppSpawningCtx *property) { APPSPAWN_LOGV("Spawning: set appEnv"); int ret = SetEnvInfo(content, property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); return 0; } static int SpawnEnableCache(AppSpawnMgr *content, AppSpawningCtx *property) { APPSPAWN_LOGV("Spawning: enable cache for app process"); // enable cache for app process mallopt(M_OHOS_CONFIG, M_TCACHE_PERFORMANCE_MODE); mallopt(M_OHOS_CONFIG, M_ENABLE_OPT_TCACHE); mallopt(M_SET_THREAD_CACHE, M_THREAD_CACHE_ENABLE); mallopt(M_DELAYED_FREE, M_DELAYED_FREE_ENABLE); int ret = SetInternetPermission(property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); return ret; } static void SpawnLoadSilk(const AppSpawnMgr *content, const AppSpawningCtx *property) { const char *processName = GetBundleName(property); APPSPAWN_CHECK(processName != NULL, return, "Can not get bundle name"); LoadSilkLibrary(processName); } static int SpawnSetProperties(AppSpawnMgr *content, AppSpawningCtx *property) { APPSPAWN_LOGV("Spawning: set child property"); SpawnLoadSilk(content, property); (void)umask(DEFAULT_UMASK); int ret = SetKeepCapabilities(content, property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); ret = SetXpmConfig(content, property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); ret = SetProcessName(content, property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); ret = SetUidGid(content, property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); ret = SetFileDescriptors(content, property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); ret = SetCapabilities(content, property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); ret = SetSelinuxCon(content, property) == -1; APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); ret = WaitForDebugger(property); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); APPSPAWN_ONLY_EXPER(GetAppSpawnMsgType(property) == MSG_SPAWN_NATIVE_PROCESS, return 0); #ifdef SECURITY_COMPONENT_ENABLE InitSecCompClientEnhance(); #endif return 0; } static int PreLoadSetSeccompFilter(AppSpawnMgr *content) { // set uid gid filetr int ret = SetUidGidFilter(content); APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret); return ret; } static int SpawnComplete(AppSpawnMgr *content, AppSpawningCtx *property) { InitDebugParams(content, property); return 0; } static int CheckEnabled(const char *param, const char *value) { char tmp[32] = {0}; // 32 max int ret = GetParameter(param, "", tmp, sizeof(tmp)); APPSPAWN_LOGV("CheckEnabled key %{public}s ret %{public}d result: %{public}s", param, ret, tmp); int enabled = (ret > 0 && strcmp(tmp, value) == 0); return enabled; } static int SpawnGetSpawningFlag(AppSpawnMgr *content, AppSpawningCtx *property) { APPSPAWN_LOGV("Spawning: prepare app %{public}s", GetProcessName(property)); if (CheckAppMsgFlagsSet(property, APP_FLAGS_COLD_BOOT)) { // check cold start property->client.flags |= CheckEnabled("startup.appspawn.cold.boot", "true") ? APP_COLD_START : 0; } if (CheckAppMsgFlagsSet(property, APP_FLAGS_BEGETCTL_BOOT)) { // Start app from begetctl for debugging. property->client.flags |= APP_BEGETCTL_BOOT; APPSPAWN_LOGI("Spawning: prepare app %{public}s, start from begetctl", GetProcessName(property)); } property->client.flags |= content->flags; return 0; } static int SpawnLoadConfig(AppSpawnMgr *content) { LoadSilkConfig(); // init flags that will not change until next reboot content->flags |= CheckEnabled("const.security.developermode.state", "true") ? APP_DEVELOPER_MODE : 0; content->flags |= CheckEnabled("persist.security.jitfort.disabled", "true") ? 0 : APP_JITFORT_MODE; return 0; } static int CloseFdArgs(AppSpawnMgr *content, AppSpawningCtx *property) { APPSPAWN_CHECK(property != NULL && property->message != NULL && property->message->connection != NULL, return -1, "Get connection info failed"); int fdCount = property->message->connection->receiverCtx.fdCount; int *fds = property->message->connection->receiverCtx.fds; if (fds != NULL && fdCount > 0) { for (int i = 0; i < fdCount; i++) { if (fds[i] > 0) { close(fds[i]); } } } property->message->connection->receiverCtx.fdCount = 0; return 0; } APPSPAWN_STATIC int SetFdEnv(AppSpawnMgr *content, AppSpawningCtx *property) { APPSPAWN_CHECK_ONLY_EXPER(property != NULL, return -1); APPSPAWN_CHECK_ONLY_EXPER(!property->isPrefork, return 0); AppSpawnMsgNode *message = property->message; APPSPAWN_CHECK_ONLY_EXPER(message != NULL && message->buffer != NULL && message->connection != NULL, return -1); APPSPAWN_CHECK_ONLY_EXPER(message->tlvOffset != NULL, return -1); int findFdIndex = 0; AppSpawnMsgReceiverCtx recvCtx = message->connection->receiverCtx; APPSPAWN_CHECK(recvCtx.fds != NULL && recvCtx.fdCount > 0, return 0, "no need set fd info %{public}d, %{public}d", recvCtx.fds != NULL, recvCtx.fdCount); char keyBuffer[APP_FDNAME_MAXLEN + sizeof(APP_FDENV_PREFIX)]; char value[sizeof(int)]; for (uint32_t index = TLV_MAX; index < (TLV_MAX + message->tlvCount); index++) { if (message->tlvOffset[index] == INVALID_OFFSET) { return -1; } uint8_t *data = message->buffer + message->tlvOffset[index]; if (((AppSpawnTlv *)data)->tlvType != TLV_MAX) { continue; } AppSpawnTlvExt *tlv = (AppSpawnTlvExt *)data; if (strcmp(tlv->tlvName, MSG_EXT_NAME_APP_FD) != 0) { continue; } APPSPAWN_CHECK(findFdIndex < recvCtx.fdCount && recvCtx.fds[findFdIndex] > 0, return -1, "check set env args failed %{public}d, %{public}d, %{public}d", findFdIndex, recvCtx.fdCount, recvCtx.fds[findFdIndex]); APPSPAWN_CHECK(snprintf_s(keyBuffer, sizeof(keyBuffer), sizeof(keyBuffer) - 1, APP_FDENV_PREFIX"%s", data + sizeof(AppSpawnTlvExt)) >= 0, return -1, "failed print env key %{public}d", errno); APPSPAWN_CHECK(snprintf_s(value, sizeof(value), sizeof(value) - 1, "%d", recvCtx.fds[findFdIndex++]) >= 0, return -1, "failed print env key %{public}d", errno); int ret = setenv(keyBuffer, value, 1); APPSPAWN_CHECK(ret == 0, return -1, "failed setenv %{public}s, %{public}s", keyBuffer, value); if (findFdIndex >= recvCtx.fdCount) { break; } } return 0; } APPSPAWN_STATIC int RecordStartTime(AppSpawnMgr *content, AppSpawningCtx *property) { struct timespec ts; int ret = clock_gettime(CLOCK_MONOTONIC, &ts); APPSPAWN_CHECK(ret == 0, return 0, "clock_gettime failed %{public}d,%{public}d", ret, errno); long long startTime = (ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000); char timeChar[32]; ret = snprintf_s(timeChar, sizeof(timeChar), sizeof(timeChar) - 1, "%lld", startTime); APPSPAWN_CHECK(ret > 0, return 0, "failed to snprintf_s %{public}d,%{public}d", ret, errno); ret = setenv(PROCESS_START_TIME_ENV, timeChar, 1); APPSPAWN_CHECK_ONLY_LOG(ret == 0, "set env failed %{public}d,%{public}d", ret, errno); return 0; } MODULE_CONSTRUCTOR(void) { APPSPAWN_LOGV("Load common module ..."); AddPreloadHook(HOOK_PRIO_COMMON, PreLoadSetSeccompFilter); AddPreloadHook(HOOK_PRIO_COMMON, SpawnLoadConfig); AddAppSpawnHook(STAGE_PARENT_PRE_FORK, HOOK_PRIO_HIGHEST, SpawnGetSpawningFlag); AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, HOOK_PRIO_HIGHEST, SpawnInitSpawningEnv); AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, HOOK_PRIO_COMMON + 1, SpawnSetAppEnv); AddAppSpawnHook(STAGE_CHILD_EXECUTE, HOOK_PRIO_HIGHEST, SpawnEnableCache); AddAppSpawnHook(STAGE_CHILD_EXECUTE, HOOK_PRIO_PROPERTY, SpawnSetProperties); AddAppSpawnHook(STAGE_CHILD_POST_RELY, HOOK_PRIO_HIGHEST, SpawnComplete); AddAppSpawnHook(STAGE_PARENT_POST_FORK, HOOK_PRIO_HIGHEST, CloseFdArgs); AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, HOOK_PRIO_HIGHEST, SetFdEnv); AddAppSpawnHook(STAGE_CHILD_PRE_RUN, HOOK_PRIO_HIGHEST, RecordStartTime); }