Merge gitee.com:openharmony/startup_appspawn into 0401_change

This commit is contained in:
zhongning5 2024-04-07 15:41:26 +08:00
commit 07b93eb1b0
43 changed files with 5374 additions and 105 deletions

View File

@ -21,6 +21,9 @@
#include <unistd.h>
#include <signal.h>
#include <malloc.h>
#include <fcntl.h>
#include <stdio.h>
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <sched.h>
@ -207,6 +210,25 @@ static int CloneAppSpawn(void *arg)
return 0;
}
static int ForkProcess(AppSandboxArg *sandbox)
{
pid_t pid = fork();
if (pid == 0) {
ProcessExit(AppSpawnChild((void *)sandbox));
}
return pid;
}
// after calling setns, new process will be in the same pid namespace of the input pid
static int SetPidNamespace(int nsPidFd, int nsType)
{
if (setns(nsPidFd, nsType) < 0) {
APPSPAWN_LOGE("set pid namespace nsType:%{pudblic}d failed", nsType);
return -1;
}
return 0;
}
int AppSpawnProcessMsg(AppSandboxArg *sandbox, pid_t *childPid)
{
APPSPAWN_CHECK(sandbox != NULL && sandbox->content != NULL, return -1, "Invalid content for appspawn");
@ -217,9 +239,12 @@ int AppSpawnProcessMsg(AppSandboxArg *sandbox, pid_t *childPid)
if (sandbox->content->isNweb) {
pid = clone(CloneAppSpawn, NULL, sandbox->client->cloneFlags | SIGCHLD, (void *)sandbox);
} else {
pid = fork();
if (pid == 0) {
ProcessExit(AppSpawnChild((void *)sandbox));
if (sandbox->content->sandboxNsFlags & CLONE_NEWPID) {
SetPidNamespace(sandbox->content->nsInitPidFd, CLONE_NEWPID); // pid_ns_init is the init process
pid = ForkProcess(sandbox);
SetPidNamespace(sandbox->content->nsSelfPidFd, 0); // go back to original pid namespace
} else {
pid = ForkProcess(sandbox);
}
}
APPSPAWN_CHECK(pid >= 0, return -errno, "fork child process error: %{public}d", -errno);

View File

@ -50,7 +50,9 @@ typedef struct AppSpawnContent {
uint32_t longProcNameLen;
uint32_t sandboxNsFlags;
bool isNweb;
int nsSelfPidFd; // ns pid fd of appspawn
int nsInitPidFd; // ns pid fd of pid_ns_init
// system
void (*loadExtendLib)(struct AppSpawnContent *content);
int (*initAppSpawn)(struct AppSpawnContent *content);

View File

@ -37,6 +37,7 @@ if (!defined(ohos_lite)) {
"${appspawn_path}/common",
"${appspawn_path}/modules/sandbox",
"${appspawn_path}/util/include",
"${appspawn_path}/modules//module_engine//include",
]
defines = [

View File

@ -1,25 +0,0 @@
[
{ "name": "AddPreloadHook" },
{ "name": "AddAppChangeHook" },
{ "name": "AddAppSpawnHook" },
{ "name": "AppSpawnHookExecute" },
{ "name": "AppSpawnEnvClear" },
{ "name": "GetAppPropertyExt" },
{ "name": "CheckAppPropertyFlags" },
{ "name": "SetAppPropertyFlags" },
{ "name": "IsNWebSpawnMode" },
{ "name": "GetAppSpawnMsgType" },
{ "name": "GetBundleName" },
{ "name": "GetAppProperty" },
{ "name": "GetProcessName" },
{ "name": "RegChildLooper" },
{ "name": "MakeDirRec" },
{ "name": "SandboxMountPath" },
{ "name": "IsDeveloperModeOn" },
{ "name": "AddVariableReplaceHandler" },
{ "name": "GetSpawnedProcess" },
{ "name": "DumpNormalProperty" },
{ "name": "AppSpawnDump" },
{ "name": "RegisterExpandSandboxCfgHandler" },
{ "name": "IsColdRunMode" }
]

View File

@ -25,7 +25,7 @@
extern "C" {
#endif
typedef struct TagSandboxSection {
typedef struct TagSandboxQueue {
struct ListNode front;
uint32_t type;
} SandboxQueue;

67
modules/ace_adapter/BUILD.gn Executable file
View File

@ -0,0 +1,67 @@
# 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.
import("//base/startup/appspawn/appspawn.gni")
import("//build/ohos.gni")
ohos_shared_library("appspawn_ace") {
sources = [
"ace_adapter.cpp",
"command_lexer.cpp",
]
include_dirs = [
".",
"${appspawn_path}/common",
"${appspawn_path}/standard",
"${appspawn_path}/util/include",
]
deps = [
"${appspawn_path}/modules/module_engine:libappspawn_module_engine",
"${appspawn_path}/util:libappspawn_util",
"//third_party/cJSON:cjson",
]
defines = []
if (asan_detector || is_asan) {
defines += [ "ASAN_DETECTOR" ]
}
external_deps = [
"ability_base:want",
"ability_runtime:app_manager",
"ability_runtime:appkit_native",
"ability_runtime:runtime",
"access_token:libtoken_setproc",
"ace_engine:ace_forward_compatibility",
"c_utils:utils",
"common_event_service:cesfwk_innerkits",
"config_policy:configpolicy_util",
"eventhandler:libeventhandler",
"hilog:libhilog",
"hitrace:hitrace_meter",
"init:libbegetutil",
"ipc:ipc_single",
"libuv:uv",
"napi:ace_napi",
"os_account:os_account_innerkits",
"resource_management:global_resmgr",
"selinux:libselinux",
]
subsystem_name = "${subsystem_name}"
part_name = "${part_name}"
install_enable = true
if (target_cpu == "arm64" || target_cpu == "x86_64" ||
target_cpu == "riscv64") {
module_install_dir = "lib64/appspawn/appspawn"
} else {
module_install_dir = "lib/appspawn/appspawn"
}
}

View File

@ -0,0 +1,204 @@
/*
* 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 <cerrno>
#include <cstring>
#include <dlfcn.h>
#include <set>
#include <string>
#include <unistd.h>
#include <utility>
#include <vector>
#include "appspawn_hook.h"
#include "appspawn_server.h"
#include "appspawn_utils.h"
#include "command_lexer.h"
#include "config_policy_utils.h"
#include "hitrace_meter.h"
#include "js_runtime.h"
#include "json_utils.h"
#include "parameters.h"
#include "resource_manager.h"
#ifndef APPSPAWN_TEST
#include "ace_forward_compatibility.h"
#include "foundation/ability/ability_runtime/interfaces/kits/native/appkit/app/main_thread.h"
#include "runtime.h"
#endif
using namespace OHOS::AppSpawn;
using namespace OHOS::Global;
#ifdef ASAN_DETECTOR
static const bool DEFAULT_PRELOAD_VALUE = false;
#else
static const bool DEFAULT_PRELOAD_VALUE = true;
#endif
static const std::string PRELOAD_JSON_CONFIG("/appspawn_preload.json");
typedef struct TagAppSpawnSandboxCfg {
std::set<std::string> modules;
} AppSpawnSandboxCfg;
static void GetModules(const cJSON *root, std::set<std::string> &modules)
{
// no config
cJSON *modulesJson = cJSON_GetObjectItemCaseSensitive(root, "napi");
if (modulesJson == nullptr) {
return;
}
uint32_t moduleCount = cJSON_GetArraySize(modulesJson);
for (uint32_t i = 0; i < moduleCount; ++i) {
const char *moduleName = cJSON_GetStringValue(cJSON_GetArrayItem(modulesJson, i));
if (moduleName == nullptr) {
continue;
}
APPSPAWN_LOGV("moduleName %{public}s", moduleName);
if (!modules.count(moduleName)) {
modules.insert(moduleName);
}
}
}
static int GetModuleSet(const cJSON *root, AppSpawnSandboxCfg *context)
{
GetModules(root, context->modules);
return 0;
}
static void PreloadModule(void)
{
OHOS::AbilityRuntime::Runtime::Options options;
options.lang = OHOS::AbilityRuntime::Runtime::Language::JS;
options.loadAce = true;
options.preload = true;
auto runtime = OHOS::AbilityRuntime::Runtime::Create(options);
if (!runtime) {
APPSPAWN_LOGE("LoadExtendLib: Failed to create runtime");
return;
}
AppSpawnSandboxCfg context = {};
(void)ParseSandboxConfig("etc/appspawn", PRELOAD_JSON_CONFIG.c_str(), GetModuleSet, &context);
for (std::string moduleName : context.modules) {
APPSPAWN_LOGI("moduleName %{public}s", moduleName.c_str());
runtime->PreloadSystemModule(moduleName);
}
// Save preloaded runtime
OHOS::AbilityRuntime::Runtime::SavePreloaded(std::move(runtime));
}
static void LoadExtendLib(void)
{
const char *acelibdir = OHOS::Ace::AceForwardCompatibility::GetAceLibName();
APPSPAWN_LOGI("LoadExtendLib: Start calling dlopen acelibdir.");
void *aceAbilityLib = dlopen(acelibdir, RTLD_NOW | RTLD_LOCAL);
APPSPAWN_CHECK(aceAbilityLib != nullptr, return, "Fail to dlopen %{public}s, [%{public}s]", acelibdir, dlerror());
APPSPAWN_LOGI("LoadExtendLib: Success to dlopen %{public}s", acelibdir);
OHOS::AppExecFwk::MainThread::PreloadExtensionPlugin();
bool preload = OHOS::system::GetBoolParameter("persist.appspawn.preload", DEFAULT_PRELOAD_VALUE);
if (!preload) {
APPSPAWN_LOGI("LoadExtendLib: Do not preload JS VM");
return;
}
APPSPAWN_LOGI("LoadExtendLib: Start preload JS VM");
SetTraceDisabled(true);
PreloadModule();
SetTraceDisabled(false);
Resource::ResourceManager *systemResMgr = Resource::GetSystemResourceManagerNoSandBox();
APPSPAWN_CHECK(systemResMgr != nullptr, return, "Fail to get system resource manager");
APPSPAWN_LOGI("LoadExtendLib: End preload JS VM");
}
static int RunChildThread(const AppSpawnMgr *content, const AppSpawningCtx *property)
{
AppSpawnEnvClear((AppSpawnContent *)&content->content, (AppSpawnClient *)&property->client);
std::string checkExit;
if (OHOS::system::GetBoolParameter("persist.init.debug.checkexit", true)) {
checkExit = std::to_string(getpid());
}
setenv(APPSPAWN_CHECK_EXIT, checkExit.c_str(), true);
OHOS::AppExecFwk::MainThread::Start();
unsetenv(APPSPAWN_CHECK_EXIT);
return 0;
}
static int RunChildByRenderCmd(const AppSpawnMgr *content, const AppSpawningCtx *property)
{
uint32_t len = 0;
char *renderCmd = reinterpret_cast<char *>(GetAppPropertyExt(property, MSG_EXT_NAME_RENDER_CMD, &len));
if (renderCmd == NULL || !IsDeveloperModeOn(property)) {
APPSPAWN_LOGE("Denied launching a native process: not in developer mode");
return -1;
}
APPSPAWN_LOGI("renderCmd %{public}s", renderCmd);
std::vector<std::string> args;
std::string command(renderCmd);
CommandLexer lexer(command);
if (!lexer.GetAllArguments(args)) {
return -1;
}
if (args.empty()) {
APPSPAWN_LOGE("Failed to run a native process: empty command %{public}s", renderCmd);
return -1;
}
std::vector<char *> options;
for (const auto &arg : args) {
options.push_back(const_cast<char *>(arg.c_str()));
}
options.push_back(nullptr);
// clear appspawn env, do not user any content and property
AppSpawnEnvClear((AppSpawnContent *)&content->content, (AppSpawnClient *)&property->client);
execvp(args[0].c_str(), options.data());
// If it succeeds calling execvp, it never returns.
int err = errno;
APPSPAWN_LOGE("Failed to launch a native process with execvp: %{public}s", strerror(err));
return 0;
}
static int RunChildProcessor(AppSpawnContent *content, AppSpawnClient *client)
{
APPSPAWN_CHECK(client != NULL && content != NULL, return -1, "Invalid client");
AppSpawningCtx *property = reinterpret_cast<AppSpawningCtx *>(client);
int ret = 0;
if (GetAppSpawnMsgType(property) == MSG_SPAWN_NATIVE_PROCESS) {
ret = RunChildByRenderCmd(reinterpret_cast<AppSpawnMgr *>(content), property);
} else {
ret = RunChildThread(reinterpret_cast<AppSpawnMgr *>(content), property);
}
return ret;
}
static int PreLoadAppSpawn(AppSpawnMgr *content)
{
if (IsNWebSpawnMode(content)) {
return 0;
}
// register
RegChildLooper(&content->content, RunChildProcessor);
LoadExtendLib();
return 0;
}
MODULE_CONSTRUCTOR(void)
{
APPSPAWN_LOGV("Load ace module ...");
AddPreloadHook(HOOK_PRIO_HIGHEST, PreLoadAppSpawn);
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2023 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 "appspawn_utils.h"
#include "command_lexer.h"
using namespace OHOS::AppSpawn;
enum class ParsingState {
INIT,
IN_WHITESPACE,
IN_ARGUMENT,
IN_SINGLE_QUOTE,
IN_DOUBLE_QUOTE,
};
bool CommandLexer::GetAllArguments(std::vector<std::string> &args)
{
constexpr char singleQuote = '\'';
constexpr char doubleQuote = '"';
ParsingState state = ParsingState::INIT;
std::string lastArg;
for (size_t i = 0; i < str_.size(); i++) {
switch (state) {
case ParsingState::INIT:
if (isspace(str_[i])) {
state = ParsingState::IN_WHITESPACE;
} else if (str_[i] == singleQuote) {
state = ParsingState::IN_SINGLE_QUOTE;
} else if (str_[i] == doubleQuote) {
state = ParsingState::IN_DOUBLE_QUOTE;
} else {
state = ParsingState::IN_ARGUMENT;
lastArg += str_[i];
}
break;
case ParsingState::IN_WHITESPACE:
if (str_[i] == singleQuote) {
state = ParsingState::IN_SINGLE_QUOTE;
} else if (str_[i] == doubleQuote) {
state = ParsingState::IN_DOUBLE_QUOTE;
} else if (!isspace(str_[i])) {
state = ParsingState::IN_ARGUMENT;
lastArg += str_[i];
}
break;
case ParsingState::IN_ARGUMENT:
if (isspace(str_[i])) {
args.push_back(std::move(lastArg));
// Whether a moved string is empty depends on the
// implementation of C++ std library, so clear() is called.
lastArg.clear();
state = ParsingState::IN_WHITESPACE;
} else if (str_[i] == singleQuote) {
state = ParsingState::IN_SINGLE_QUOTE;
} else if (str_[i] == doubleQuote) {
state = ParsingState::IN_DOUBLE_QUOTE;
} else {
lastArg += str_[i];
}
break;
case ParsingState::IN_SINGLE_QUOTE:
if (str_[i] == singleQuote) {
state = ParsingState::IN_ARGUMENT;
} else {
lastArg += str_[i];
}
break;
case ParsingState::IN_DOUBLE_QUOTE:
if (str_[i] == doubleQuote) {
state = ParsingState::IN_ARGUMENT;
} else {
lastArg += str_[i];
}
break;
}
}
if (state == ParsingState::IN_ARGUMENT) {
args.push_back(std::move(lastArg));
} else if (state == ParsingState::IN_SINGLE_QUOTE ||
state == ParsingState::IN_DOUBLE_QUOTE) {
APPSPAWN_LOGE("Parsing arguments failed: missing terminated quote");
args.clear();
return false;
}
return true;
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2023 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.
*/
#ifndef COMMAND_LEXER_H
#define COMMAND_LEXER_H
#include <string>
#include <vector>
namespace OHOS {
namespace AppSpawn {
class CommandLexer {
public:
explicit CommandLexer(const std::string &str) : str_(str) {}
// Disable copy and move semantics to avoid extension of the lifecyle of the
// resource not owned by this class.
CommandLexer(const CommandLexer &other) = delete;
CommandLexer(CommandLexer &&other) = delete;
CommandLexer &operator=(const CommandLexer &other) = delete;
CommandLexer &operator=(CommandLexer &&other) = delete;
// Return true, and args will be populated with all argument. Otherwise,
// false is returned, and args will be cleared.
bool GetAllArguments(std::vector<std::string> &args);
private:
const std::string &str_;
};
} // namespace AppSpawn
} // namespace OHOS
#endif // COMMAND_LEXER_H

46
modules/asan/BUILD.gn Executable file
View File

@ -0,0 +1,46 @@
# 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.
import("//base/startup/appspawn/appspawn.gni")
import("//build/ohos.gni")
ohos_shared_library("appspawn_asan") {
sources = [ "asan_detector.c" ]
include_dirs = [
".",
"${appspawn_path}/common",
"${appspawn_path}/standard",
]
defines = []
deps = [ "${appspawn_path}/modules/module_engine:libappspawn_module_engine" ]
if (is_asan) {
defines += [ "APPSPAWN_ASAN" ]
}
if (asan_detector || is_asan) {
defines += [ "ASAN_DETECTOR" ]
}
external_deps = [
"c_utils:utils",
"hilog:libhilog",
"init:libbegetutil",
]
install_enable = true
subsystem_name = "${subsystem_name}"
part_name = "${part_name}"
if (target_cpu == "arm64" || target_cpu == "x86_64" ||
target_cpu == "riscv64") {
module_install_dir = "lib64/appspawn"
} else {
module_install_dir = "lib/appspawn"
}
}

120
modules/asan/asan_detector.c Executable file
View File

@ -0,0 +1,120 @@
/*
* 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 "appspawn_hook.h"
#include "appspawn_msg.h"
#include "appspawn_utils.h"
#include "parameter.h"
#include "securec.h"
// for stub
extern bool may_init_gwp_asan(bool forceInit);
// ide-asan
static int SetAsanEnabledEnv(const AppSpawnMgr *content, const AppSpawningCtx *property)
{
const char *bundleName = GetBundleName(property);
if (CheckAppMsgFlagsSet(property, APP_FLAGS_ASANENABLED)) {
char *devPath = "/dev/asanlog";
char logPath[PATH_MAX] = {0};
int ret = snprintf_s(logPath, sizeof(logPath), sizeof(logPath) - 1,
"/data/app/el1/100/base/%s/log", bundleName);
APPSPAWN_CHECK(ret > 0, return -1, "Invalid snprintf_s");
char asanOptions[PATH_MAX] = {0};
ret = snprintf_s(asanOptions, sizeof(asanOptions), sizeof(asanOptions) - 1,
"log_path=%s/asan.log:include=/system/etc/asan.options", devPath);
APPSPAWN_CHECK(ret > 0, return -1, "Invalid snprintf_s");
#if defined(__aarch64__) || defined(__x86_64__)
setenv("LD_PRELOAD", "/system/lib64/libclang_rt.asan.so", 1);
#else
setenv("LD_PRELOAD", "/system/lib/libclang_rt.asan.so", 1);
#endif
unsetenv("UBSAN_OPTIONS");
setenv("ASAN_OPTIONS", asanOptions, 1);
return 0;
}
return -1;
}
static void SetGwpAsanEnabled(const AppSpawnMgr *content, const AppSpawningCtx *property)
{
if (!(CheckAppMsgFlagsSet(property, APP_FLAGS_GWP_ENABLED_FORCE) ||
CheckAppMsgFlagsSet(property, APP_FLAGS_GWP_ENABLED_NORMAL))) {
return;
}
if (IsDeveloperModeOn(property)) {
APPSPAWN_LOGV("SetGwpAsanEnabled with flags: %{public}d",
CheckAppMsgFlagsSet(property, APP_FLAGS_GWP_ENABLED_FORCE));
may_init_gwp_asan(CheckAppMsgFlagsSet(property, APP_FLAGS_GWP_ENABLED_FORCE));
}
}
#ifdef ASAN_DETECTOR
#define WRAP_VALUE_MAX_LENGTH 96
static int CheckSupportColdStart(const char *bundleName)
{
char wrapBundleNameKey[WRAP_VALUE_MAX_LENGTH] = {0};
char wrapBundleNameValue[WRAP_VALUE_MAX_LENGTH] = {0};
int len = sprintf_s(wrapBundleNameKey, WRAP_VALUE_MAX_LENGTH, "wrap.%s", bundleName);
APPSPAWN_CHECK(len > 0 && (len < WRAP_VALUE_MAX_LENGTH), return -1, "Invalid to format wrapBundleNameKey");
int ret = GetParameter(wrapBundleNameKey, "", wrapBundleNameValue, WRAP_VALUE_MAX_LENGTH);
APPSPAWN_CHECK(ret > 0 && (!strcmp(wrapBundleNameValue, "asan_wrapper")), return -1,
"Not wrap %{public}s.", bundleName);
APPSPAWN_LOGI("Asan: GetParameter %{public}s the value is %{public}s.", wrapBundleNameKey, wrapBundleNameValue);
return 0;
}
#endif
static int AsanSpawnGetSpawningFlag(AppSpawnMgr *content, AppSpawningCtx *property)
{
APPSPAWN_LOGV("Prepare spawn app %{public}s", GetProcessName(property));
#ifdef ASAN_DETECTOR
if (CheckSupportColdStart(GetBundleName(property)) == 0) {
property->client.flags |= APP_COLD_START;
property->client.flags |= APP_ASAN_DETECTOR;
if (property->forkCtx.coldRunPath) {
free(property->forkCtx.coldRunPath);
}
property->forkCtx.coldRunPath = strdup("/system/asan/bin/appspawn");
if (property->forkCtx.coldRunPath == NULL) {
APPSPAWN_LOGE("Failed to set asan exec path %{public}s", GetProcessName(property));
}
}
#endif
return 0;
}
static int AsanSpawnInitSpawningEnv(AppSpawnMgr *content, AppSpawningCtx *property)
{
if (GetAppSpawnMsgType(property) == MSG_SPAWN_NATIVE_PROCESS) {
return 0;
}
int ret = SetAsanEnabledEnv(content, property);
if (ret == 0) {
APPSPAWN_LOGI("SetAsanEnabledEnv cold start app %{public}s", GetProcessName(property));
property->client.flags |= APP_COLD_START;
}
(void)SetGwpAsanEnabled(content, property);
return 0;
}
MODULE_CONSTRUCTOR(void)
{
APPSPAWN_LOGV("Load asan module ...");
AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, HOOK_PRIO_COMMON, AsanSpawnInitSpawningEnv);
AddAppSpawnHook(STAGE_PARENT_PRE_FORK, HOOK_PRIO_COMMON, AsanSpawnGetSpawningFlag);
}

73
modules/common/BUILD.gn Executable file
View File

@ -0,0 +1,73 @@
# 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.
import("//base/startup/appspawn/appspawn.gni")
import("//build/ohos.gni")
ohos_shared_library("appspawn_common") {
sources = [
"appspawn_adapter.cpp",
"appspawn_cgroup.c",
"appspawn_common.c",
]
include_dirs = [
".",
"${appspawn_path}/common",
"${appspawn_path}/standard",
]
cflags = []
deps = [
"${appspawn_path}/modules/module_engine:libappspawn_module_engine",
"//third_party/cJSON:cjson",
]
defines = [ "GRAPHIC_PERMISSION_CHECK" ]
external_deps = [
"access_token:libtoken_setproc",
"access_token:libtokenid_sdk",
"c_utils:utils",
"hilog:libhilog",
"init:libbegetutil",
"netmanager_base:netsys_client",
]
if (build_selinux) {
defines += [ "WITH_SELINUX" ]
deps += [ "//third_party/selinux:libselinux" ]
external_deps += [ "selinux_adapter:libhap_restorecon" ]
}
if (build_seccomp) {
defines += [ "WITH_SECCOMP" ]
external_deps += [ "init:seccomp" ]
}
if (!defined(global_parts_info) ||
defined(global_parts_info.security_security_component_manager)) {
defines += [ "SECURITY_COMPONENT_ENABLE" ]
external_deps += [ "security_component_manager:libsecurity_component_sdk" ]
}
if (!defined(global_parts_info) ||
defined(global_parts_info.security_code_signature)) {
defines += [ "CODE_SIGNATURE_ENABLE" ]
external_deps += [ "code_signature:libcode_sign_attr_utils" ]
}
subsystem_name = "${subsystem_name}"
part_name = "${part_name}"
install_enable = true
if (target_cpu == "arm64" || target_cpu == "x86_64" ||
target_cpu == "riscv64") {
module_install_dir = "lib64/appspawn/common"
} else {
module_install_dir = "lib/appspawn/common"
}
}

View File

@ -0,0 +1,168 @@
/*
* 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 "appspawn_adapter.h"
#include "access_token.h"
#include "appspawn_hook.h"
#include "appspawn_utils.h"
#include "cJSON.h"
#include "token_setproc.h"
#include "tokenid_kit.h"
#ifdef WITH_SELINUX
#include "hap_restorecon.h"
#include "selinux/selinux.h"
#endif
#ifdef WITH_SECCOMP
#include "seccomp_policy.h"
#include <sys/prctl.h>
const char *RENDERER_NAME = "renderer";
#endif
#define NWEBSPAWN_SERVER_NAME "nwebspawn"
using namespace OHOS::Security::AccessToken;
int SetAppAccessToken(const AppSpawnMgr *content, const AppSpawningCtx *property)
{
int32_t ret = 0;
uint64_t tokenId = 0;
AppSpawnMsgAccessToken *tokenInfo = (AppSpawnMsgAccessToken *)GetAppProperty(property, TLV_ACCESS_TOKEN_INFO);
APPSPAWN_CHECK(tokenInfo != NULL, return APPSPAWN_MSG_INVALID,
"No access token in msg %{public}s", GetProcessName(property));
APPSPAWN_LOGV("AppSpawnServer::set access token %{public}" PRId64 " %{public}d",
tokenInfo->accessTokenIdEx, IsNWebSpawnMode(content));
if (IsNWebSpawnMode(content)) {
TokenIdKit tokenIdKit;
tokenId = tokenIdKit.GetRenderTokenID(tokenInfo->accessTokenIdEx);
} else {
tokenId = tokenInfo->accessTokenIdEx;
}
ret = SetSelfTokenID(tokenId);
APPSPAWN_CHECK(ret == 0, return APPSPAWN_ACCESS_TOKEN_INVALID,
"set access token id failed, ret: %{public}d %{public}s", ret, GetProcessName(property));
APPSPAWN_LOGV("SetAppAccessToken success for %{public}s", GetProcessName(property));
return 0;
}
int SetSelinuxCon(const AppSpawnMgr *content, const AppSpawningCtx *property)
{
#ifdef WITH_SELINUX
APPSPAWN_LOGV("SetSelinuxCon IsDeveloperModeOn %{public}d", IsDeveloperModeOn(property));
if (GetAppSpawnMsgType(property) == MSG_SPAWN_NATIVE_PROCESS) {
if (!IsDeveloperModeOn(property)) {
APPSPAWN_LOGE("Denied Launching a native process: not in developer mode");
return APPSPAWN_NATIVE_NOT_SUPPORT;
}
return 0;
}
if (IsNWebSpawnMode(content)) {
setcon("u:r:isolated_render:s0");
return 0;
}
AppSpawnMsgDomainInfo *msgDomainInfo = (AppSpawnMsgDomainInfo *)GetAppProperty(property, TLV_DOMAIN_INFO);
APPSPAWN_CHECK(msgDomainInfo != NULL, return APPSPAWN_TLV_NONE,
"No domain info in req form %{public}s", GetProcessName(property))
HapContext hapContext;
HapDomainInfo hapDomainInfo;
hapDomainInfo.apl = msgDomainInfo->apl;
hapDomainInfo.packageName = GetProcessName(property);
hapDomainInfo.hapFlags = msgDomainInfo->hapFlags;
if (CheckAppMsgFlagsSet(property, APP_FLAGS_DEBUGGABLE)) {
hapDomainInfo.hapFlags |= SELINUX_HAP_DEBUGGABLE;
}
int32_t ret = hapContext.HapDomainSetcontext(hapDomainInfo);
if (CheckAppMsgFlagsSet(property, APP_FLAGS_ASANENABLED)) {
ret = 0;
}
APPSPAWN_CHECK(ret == 0, return APPSPAWN_ACCESS_TOKEN_INVALID,
"Set domain context failed, ret: %{public}d %{public}s", ret, GetProcessName(property));
APPSPAWN_LOGV("SetSelinuxCon success for %{public}s", GetProcessName(property));
#endif
return 0;
}
int SetUidGidFilter(const AppSpawnMgr *content)
{
#ifdef WITH_SECCOMP
bool ret = false;
if (IsNWebSpawnMode(content)) {
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
APPSPAWN_LOGE("Failed to set no new privs");
}
ret = SetSeccompPolicyWithName(INDIVIDUAL, NWEBSPAWN_NAME);
} else {
ret = SetSeccompPolicyWithName(INDIVIDUAL, APPSPAWN_NAME);
}
if (!ret) {
APPSPAWN_LOGE("Failed to set APPSPAWN seccomp filter and exit");
_exit(0x7f);
}
APPSPAWN_LOGV("SetUidGidFilter success");
#endif
return 0;
}
int SetSeccompFilter(const AppSpawnMgr *content, const AppSpawningCtx *property)
{
#ifdef WITH_SECCOMP
const char *appName = APP_NAME;
SeccompFilterType type = APP;
if (IsNWebSpawnMode(content)) {
return 0;
}
if (!SetSeccompPolicyWithName(type, appName)) {
APPSPAWN_LOGE("Failed to set %{public}s seccomp filter and exit %{public}d", appName, errno);
return -EINVAL;
}
APPSPAWN_LOGV("SetSeccompFilter success for %{public}s", GetProcessName(property));
#endif
return 0;
}
int SetInternetPermission(const AppSpawningCtx *property)
{
AppSpawnMsgInternetInfo *info = (AppSpawnMsgInternetInfo *)GetAppProperty(property, TLV_INTERNET_INFO);
APPSPAWN_CHECK(info != NULL, return 0,
"No tlv internet permission info in req form %{public}s", GetProcessName(property));
APPSPAWN_LOGV("Set internet permission %{public}d %{public}d", info->setAllowInternet, info->allowInternet);
if (info->setAllowInternet == 1 && info->allowInternet == 0) {
DisallowInternet();
}
return 0;
}
int32_t SetEnvInfo(const AppSpawnMgr *content, const AppSpawningCtx *property)
{
uint32_t size = 0;
char *envStr = reinterpret_cast<char *>(GetAppPropertyExt(property, "AppEnv", &size));
if (size == 0 || envStr == NULL) {
return 0;
}
int ret = 0;
cJSON *root = cJSON_Parse(envStr);
APPSPAWN_CHECK(root != nullptr, return -1, "SetEnvInfo: json parse failed %{public}s", envStr);
cJSON *config = nullptr;
cJSON_ArrayForEach(config, root) {
const char *name = config->string;
const char *value = cJSON_GetStringValue(config);
APPSPAWN_LOGV("SetEnvInfo name: %{public}s value: %{public}s", name, value);
ret = setenv(name, value, 1);
APPSPAWN_CHECK(ret == 0, break, "setenv failed, errno: %{public}d", errno);
}
cJSON_Delete(root);
return ret;
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021-2022 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.
*/
#ifndef APPSPAWN_ADPATER_CPP
#define APPSPAWN_ADPATER_CPP
#ifdef __cplusplus
extern "C" {
#endif
int SetAppAccessToken(const AppSpawnMgr *content, const AppSpawningCtx *property);
int SetSelinuxCon(const AppSpawnMgr *content, const AppSpawningCtx *property);
int SetUidGidFilter(const AppSpawnMgr *content);
int SetSeccompFilter(const AppSpawnMgr *content, const AppSpawningCtx *property);
int SetInternetPermission(const AppSpawningCtx *property);
int32_t SetEnvInfo(const AppSpawnMgr *content, const AppSpawningCtx *property);
// stub for extend func
void DisallowInternet(void);
#ifdef __cplusplus
}
#endif
#endif

158
modules/common/appspawn_cgroup.c Executable file
View File

@ -0,0 +1,158 @@
/*
* 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 <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "appspawn_adapter.h"
#include "appspawn_hook.h"
#include "appspawn_utils.h"
#include "securec.h"
APPSPAWN_STATIC int GetCgroupPath(const AppSpawnedProcessInfo *appInfo, char *buffer, uint32_t buffLen)
{
const int userId = appInfo->uid / UID_BASE;
#ifdef APPSPAWN_TEST
int ret = snprintf_s(buffer, buffLen, buffLen - 1, APPSPAWN_BASE_DIR "/dev/pids/testpids/%d/%s/%d/",
userId, appInfo->name, appInfo->pid);
#else
int ret = snprintf_s(buffer, buffLen, buffLen - 1, "/dev/pids/%d/%s/app_%d/", userId, appInfo->name, appInfo->pid);
#endif
APPSPAWN_CHECK(ret > 0, return ret, "Failed to snprintf_s errno: %{public}d", errno);
APPSPAWN_LOGV("Cgroup path %{public}s ", buffer);
return 0;
}
APPSPAWN_STATIC int WriteToFile(const char *path, int truncated, pid_t pids[], uint32_t count)
{
char pidName[32] = {0}; // 32 max len
int fd = open(path, O_RDWR | (truncated ? O_TRUNC : O_APPEND));
APPSPAWN_CHECK(fd >= 0, return APPSPAWN_SYSTEM_ERROR,
"Failed to open file errno: %{public}d path: %{public}s", errno, path);
int ret = 0;
for (uint32_t i = 0; i < count; i++) {
APPSPAWN_LOGV(" WriteToFile pid %{public}d ", pids[i]);
ret = snprintf_s(pidName, sizeof(pidName), sizeof(pidName) - 1, "%d\n", pids[i]);
APPSPAWN_CHECK(ret > 0, break, "Failed to snprintf_s errno: %{public}d", errno);
ret = write(fd, pidName, strlen(pidName));
APPSPAWN_CHECK(ret > 0, break,
"Failed to write file errno: %{public}d path: %{public}s %{public}s", errno, path, pidName);
ret = 0;
}
close(fd);
return ret;
}
static int WritePidMax(const char *path, uint32_t max)
{
char value[32] = {0}; // 32 max len
int fd = open(path, O_RDWR | O_TRUNC);
APPSPAWN_CHECK(fd >= 0, return -1,
"Failed to open file errno: %{public}d path: %{public}s", errno, path);
int ret = 0;
do {
ret = snprintf_s(value, sizeof(value), sizeof(value) - 1, "%u\n", max);
APPSPAWN_CHECK(ret > 0, break, "Failed to snprintf_s errno: %{public}d", errno);
ret = write(fd, value, strlen(value));
APPSPAWN_CHECK(ret > 0, break,
"Failed to write file errno: %{public}d path: %{public}s %{public}s %{public}d", errno, path, value, ret);
ret = 0;
} while (0);
close(fd);
return ret;
}
static void KillProcessesByCGroup(const char *path, AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo)
{
FILE *file = fopen(path, "r");
APPSPAWN_CHECK(file != NULL, return, "Open file fail %{public}s errno: %{public}d", path, errno);
pid_t pid = 0;
while (fscanf_s(file, "%d\n", &pid) == 1 && pid > 0) {
APPSPAWN_LOGV(" KillProcessesByCGroup pid %{public}d ", pid);
if (pid == appInfo->pid) {
continue;
}
AppSpawnedProcessInfo *tmp = GetSpawnedProcess(pid);
if (tmp != NULL) {
APPSPAWN_LOGI("Got app %{public}s in same group for pid %{public}d.", tmp->name, pid);
continue;
}
APPSPAWN_LOGI("Kill app pid %{public}d now ...", pid);
#ifdef APPSPAWN_TEST
kill(pid, SIGKILL);
#endif
}
(void)fclose(file);
}
static int ProcessMgrRemoveApp(const AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo)
{
APPSPAWN_CHECK_ONLY_EXPER(content != NULL, return -1);
APPSPAWN_CHECK_ONLY_EXPER(appInfo != NULL, return -1);
if (IsNWebSpawnMode(content) || strcmp(appInfo->name, NWEBSPAWN_SERVER_NAME) == 0) {
return 0;
}
char path[PATH_MAX] = {};
APPSPAWN_LOGV("ProcessMgrRemoveApp %{public}d %{public}d to cgroup ", appInfo->pid, appInfo->uid);
int ret = GetCgroupPath(appInfo, path, sizeof(path));
APPSPAWN_CHECK(ret == 0, return -1, "Failed to get real path errno: %d", errno);
ret = strcat_s(path, sizeof(path), "cgroup.procs");
APPSPAWN_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %{public}d", errno);
KillProcessesByCGroup(path, (AppSpawnMgr *)content, appInfo);
return ret;
}
static int ProcessMgrAddApp(const AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo)
{
APPSPAWN_CHECK_ONLY_EXPER(content != NULL, return -1);
APPSPAWN_CHECK_ONLY_EXPER(appInfo != NULL, return -1);
if (IsNWebSpawnMode(content)) {
return 0;
}
char path[PATH_MAX] = {};
APPSPAWN_LOGV("ProcessMgrAddApp %{public}d %{public}d to cgroup ", appInfo->pid, appInfo->uid);
int ret = GetCgroupPath(appInfo, path, sizeof(path));
APPSPAWN_CHECK(ret == 0, return -1, "Failed to get real path errno: %d", errno);
(void)CreateSandboxDir(path, 0755); // 0755 default mode
uint32_t pathLen = strlen(path);
ret = strcat_s(path, sizeof(path), "cgroup.procs");
APPSPAWN_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %{public}d", errno);
ret = WriteToFile(path, 0, (pid_t *)&appInfo->pid, 1);
APPSPAWN_CHECK(ret == 0, return ret, "write pid to cgroup.procs fail %{public}s", path);
if (appInfo->max != 0) {
path[pathLen] = '\0';
ret = strcat_s(path, sizeof(path), "pids.max");
APPSPAWN_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %{public}d", errno);
ret = WritePidMax(path, appInfo->max);
APPSPAWN_CHECK(ret == 0, return ret, "write max to pids.max fail %{public}s", path);
}
APPSPAWN_LOGV("Add app %{public}d to cgroup %{public}s success", appInfo->pid, path);
return 0;
}
MODULE_CONSTRUCTOR(void)
{
AddProcessMgrHook(STAGE_SERVER_APP_ADD, 0, ProcessMgrAddApp);
AddProcessMgrHook(STAGE_SERVER_APP_DIED, 0, ProcessMgrRemoveApp);
}

467
modules/common/appspawn_common.c Executable file
View File

@ -0,0 +1,467 @@
/*
* 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 <fcntl.h>
#include <grp.h>
#include <inttypes.h>
#include <limits.h>
#include <sys/capability.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/signalfd.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <dlfcn.h>
#include <malloc.h>
#include <sched.h>
#include "appspawn_adapter.h"
#include "appspawn_hook.h"
#include "appspawn_msg.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 DEVICE_NULL_STR "/dev/null"
#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
close(property->forkCtx.fd[0]);
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;
}
AppSpawnMsgOwnerId *ownerInfo = (AppSpawnMsgOwnerId *)GetAppProperty(property, TLV_OWNER_INFO);
int ret = InitXpmRegion();
APPSPAWN_CHECK(ret == 0, return ret, "init xpm region failed: %{public}d", ret);
if (CheckAppMsgFlagsSet(property, APP_FLAGS_DEBUGGABLE)) {
ret = SetXpmOwnerId(PROCESS_OWNERID_DEBUG, NULL);
} else if (ownerInfo == NULL) {
ret = SetXpmOwnerId(PROCESS_OWNERID_COMPAT, NULL);
} else {
ret = SetXpmOwnerId(PROCESS_OWNERID_APP, ownerInfo->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)
{
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
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));
}
}
return 0;
}
static int32_t SetFileDescriptors(const AppSpawnMgr *content, const AppSpawningCtx *property)
{
#ifndef APPSPAWN_TEST
// close stdin stdout stderr
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// redirect 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);
};
#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));
(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";
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 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 int SpawnSetProperties(AppSpawnMgr *content, AppSpawningCtx *property)
{
APPSPAWN_LOGV("Spawning: set child 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 = SetEnvInfo(content, property);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
ret = WaitForDebugger(property);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
#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;
}
// check developer mode
property->client.flags |= CheckEnabled("const.security.developermode.state", "true") ? APP_DEVELOPER_MODE : 0;
return 0;
}
static int PreLoadEnablePidNs(AppSpawnMgr *content)
{
APPSPAWN_LOGI("Enable pid namespace %{public}d %{public}d sandboxNsFlags: %{public}x",
IsNWebSpawnMode(content), IsColdRunMode(content), content->content.sandboxNsFlags);
if (IsNWebSpawnMode(content) || IsColdRunMode(content)) {
return 0;
}
if (!(content->content.sandboxNsFlags & CLONE_NEWPID)) {
return 0;
}
int ret = unshare(CLONE_NEWPID);
APPSPAWN_CHECK(ret == 0, return -1, "unshare CLONE_NWEPID failed, errno=%{public}d", errno);
pid_t pid = fork();
if (pid == 0) {
setuid(PID_NS_INIT_UID);
setgid(PID_NS_INIT_GID);
#ifdef WITH_SELINUX
setcon("u:r:pid_ns_init:s0");
#endif
char *argv[] = {"/system/bin/pid_ns_init", NULL};
execve("/system/bin/pid_ns_init", argv, NULL);
_exit(0x7f);
}
return 0;
}
MODULE_CONSTRUCTOR(void)
{
APPSPAWN_LOGV("Load common module ...");
AddPreloadHook(HOOK_PRIO_COMMON, PreLoadSetSeccompFilter);
AddPreloadHook(HOOK_PRIO_LOWEST, PreLoadEnablePidNs);
AddAppSpawnHook(STAGE_PARENT_PRE_FORK, HOOK_PRIO_HIGHEST, SpawnGetSpawningFlag);
AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, HOOK_PRIO_HIGHEST, SpawnInitSpawningEnv);
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);
}

View File

@ -23,7 +23,7 @@ config("appspawn_module_engine_exported_config") {
visibility += [ "include" ]
include_dirs = [
"${appspawn_innerkits_path}/include",
"${appspawn_innerkits_path}/module_engine/include",
"${appspawn_path}/modules/module_engine/include",
"${appspawn_path}/util/include",
]
}

View File

@ -35,7 +35,7 @@ typedef struct TagAppSpawnMgr AppSpawnMgr;
typedef struct TagAppSpawningCtx AppSpawningCtx;
typedef struct AppSpawnContent AppSpawnContent;
typedef struct AppSpawnClient AppSpawnClient;
typedef struct TagAppSpawnedProcess AppSpawnedProcess;
typedef struct TagAppSpawnedProcess AppSpawnedProcessInfo;
typedef enum {
EXT_DATA_SANDBOX
@ -44,76 +44,48 @@ typedef enum {
struct TagAppSpawnExtData;
typedef void (*AppSpawnExtDataFree)(struct TagAppSpawnExtData *data);
typedef void (*AppSpawnExtDataDump)(struct TagAppSpawnExtData *data);
typedef void (*AppSpawnExtDataClear)(struct TagAppSpawnExtData *data);
typedef struct TagAppSpawnExtData {
ListNode node;
uint32_t dataId;
AppSpawnExtDataFree freeNode;
AppSpawnExtDataClear clearNode;
AppSpawnExtDataDump dumpNode;
} AppSpawnExtData;
typedef enum TagAppSpawnHookStage {
// run in init
HOOK_PRELOAD = 10,
STAGE_SERVER_PRELOAD = 10,
STAGE_SERVER_APP_ADD,
STAGE_SERVER_APP_DIED,
// run before fork
HOOK_SPAWN_PREPARE = 20,
STAGE_PARENT_PRE_FORK = 20,
STAGE_PARENT_PRE_RELY = 21,
STAGE_PARENT_POST_RELY = 22,
// run in child process
HOOK_SPAWN_CLEAR_ENV = 30, // clear env, set token HOOK_SPAWN_CLEAR_ENV
HOOK_SPAWN_SET_CHILD_PROPERTY,
HOOK_SPAWN_COMPLETED,
HOOK_SPAWN_POST = 40,
// for app change
HOOK_APP_ADD = 50,
HOOK_APP_DIED,
STAGE_CHILD_PRE_COLDBOOT = 30, // clear env, set token before cold boot
STAGE_CHILD_EXECUTE,
STAGE_CHILD_PRE_RELY,
STAGE_CHILD_POST_RELY,
STAGE_CHILD_PRE_RUN
} AppSpawnHookStage;
typedef enum TagAppSpawnHookPrio {
HOOK_PRIO_HIGHEST = 1000,
HOOK_PRIO_COMMON = 2000,
HOOK_PRIO_SANDBOX = 3000,
HOOK_PRIO_PROPERTY = 4000,
HOOK_PRIO_LOWEST = 5000,
} AppSpawnHookPrio;
typedef int (*PreloadHook)(AppSpawnMgr *content);
typedef int (*AppSpawnHook)(AppSpawnMgr *content, AppSpawningCtx *property);
typedef int (*ProcessChangeHook)(const AppSpawnMgr *content, const AppSpawnedProcess *appInfo);
typedef int (*ProcessChangeHook)(const AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo);
int AddPreloadHook(int prio, PreloadHook hook);
int AddAppSpawnHook(AppSpawnHookStage stage, int prio, AppSpawnHook hook);
int AddAppChangeHook(AppSpawnHookStage stage, int prio, ProcessChangeHook hook);
int IsNWebSpawnMode(const AppSpawnMgr *content);
int IsColdRunMode(const AppSpawnMgr *content);
int GetAppSpawnMsgType(const AppSpawningCtx *property);
const char *GetBundleName(const AppSpawningCtx *property);
void *GetAppProperty(const AppSpawningCtx *property, uint32_t type);
const char *GetProcessName(const AppSpawningCtx *property);
/**
* @brief Get the App Property Ex info
*
* @param property app
* @param name
* @param len
* @return uint8_t*
*/
uint8_t *GetAppPropertyExt(const AppSpawningCtx *property, const char *name, uint32_t *len);
/**
* @brief app属性参数的flags是否设置
*
* @param property app
* @param type TLV_MSG_FLAGS or TLV_PERMISSION
* @param index flags index
* @return int
*/
int CheckAppPropertyFlags(const AppSpawningCtx *property, uint32_t type, uint32_t index);
int SetAppPropertyFlags(const AppSpawningCtx *property, uint32_t type, uint32_t index);
__attribute__((always_inline)) inline int TestAppMsgFlagsSet(const AppSpawningCtx *property, uint32_t index)
{
return CheckAppPropertyFlags(property, TLV_MSG_FLAGS, index);
}
__attribute__((always_inline)) inline int TestAppPermissionFlags(const AppSpawningCtx *property, uint32_t index)
{
return CheckAppPropertyFlags(property, TLV_PERMISSION, index);
}
__attribute__((always_inline)) inline int SetAppPermissionFlags(const AppSpawningCtx *property, uint32_t index)
{
return SetAppPropertyFlags(property, TLV_PERMISSION, index);
}
int AddProcessMgrHook(AppSpawnHookStage stage, int prio, ProcessChangeHook hook);
typedef int (*ChildLoop)(AppSpawnContent *content, AppSpawnClient *client);
/**
@ -133,7 +105,7 @@ void RegChildLooper(AppSpawnContent *content, ChildLoop loop);
* @return int
*/
int MakeDirRec(const char *path, mode_t mode, int lastPath);
__attribute__((always_inline)) inline int MakeDirRecursive(const char *path, mode_t mode)
__attribute__((always_inline)) inline int CreateSandboxDir(const char *path, mode_t mode)
{
return MakeDirRec(path, mode, 1);
}
@ -148,12 +120,12 @@ typedef struct {
} MountArg;
int SandboxMountPath(const MountArg *arg);
int IsDeveloperModeOn(const AppSpawningCtx *property);
// 扩展变量
typedef struct TagSandboxContext SandboxContext;
typedef struct TagVarExtraData VarExtraData;
typedef int (*ReplaceVarHandler)(const SandboxContext *context,
const uint8_t *buffer, uint32_t bufferLen, uint32_t *realLen, int permission);
const char *buffer, uint32_t bufferLen, uint32_t *realLen, const VarExtraData *extraData);
/**
* @brief
*
@ -163,9 +135,9 @@ typedef int (*ReplaceVarHandler)(const SandboxContext *context,
*/
int AddVariableReplaceHandler(const char *name, ReplaceVarHandler handler);
typedef struct TagAppSpawnSandbox AppSpawnSandbox;
typedef struct TagAppSpawnSandboxCfg AppSpawnSandboxCfg;
typedef int (*ProcessExpandSandboxCfg)(const SandboxContext *context,
const AppSpawnSandbox *appSandBox, const char *name);
const AppSpawnSandboxCfg *appSandBox, const char *name);
#define EXPAND_CFG_HANDLER_PRIO_START 3
/**

View File

@ -0,0 +1,19 @@
[
{ "name": "AddPreloadHook" },
{ "name": "AddProcessMgrHook" },
{ "name": "AddAppSpawnHook" },
{ "name": "AppSpawnHookExecute" },
{ "name": "AppSpawnEnvClear" },
{ "name": "GetAppSpawnMsgInfo" },
{ "name": "GetAppSpawnMsgExtInfo" },
{ "name": "CheckAppSpawnMsgFlag" },
{ "name": "SetAppSpawnMsgFlag" },
{ "name": "RegChildLooper" },
{ "name": "MakeDirRec" },
{ "name": "SandboxMountPath" },
{ "name": "AddVariableReplaceHandler" },
{ "name": "GetSpawnedProcess" },
{ "name": "DumpAppSpawnMsg" },
{ "name": "AppSpawnDump" },
{ "name": "RegisterExpandSandboxCfgHandler" }
]

View File

@ -0,0 +1,260 @@
/*
* 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 "appspawn_modulemgr.h"
#include "appspawn_hook.h"
#include "appspawn_utils.h"
#include "hookmgr.h"
#include "modulemgr.h"
typedef struct {
const AppSpawnContent *content;
const AppSpawnedProcessInfo *appInfo;
} AppSpawnAppArg;
static struct {
MODULE_MGR *moduleMgr;
AppSpawnModuleType type;
const char *moduleName;
} g_moduleMgr[MODULE_MAX] = {
{NULL, MODULE_DEFAULT, "appspawn"},
{NULL, MODULE_APPSPAWN, "appspawn/appspawn"},
{NULL, MODULE_NWEBSPAWN, "appspawn/nwebspawn"},
{NULL, MODULE_COMMON, "appspawn/common"},
};
static HOOK_MGR *g_appspawnHookMgr = NULL;
int AppSpawnModuleMgrInstall(const char *moduleName)
{
if (moduleName == NULL) {
return -1;
}
int type = MODULE_DEFAULT;
if (g_moduleMgr[type].moduleMgr == NULL) {
g_moduleMgr[type].moduleMgr = ModuleMgrCreate(g_moduleMgr[type].moduleName);
}
if (g_moduleMgr[type].moduleMgr == NULL) {
return -1;
}
#ifndef APPSPAWN_TEST
return ModuleMgrInstall(g_moduleMgr[type].moduleMgr, moduleName, 0, NULL);
#else
return 0;
#endif
}
void AppSpawnModuleMgrUnInstall(int type)
{
if (type >= MODULE_MAX) {
return;
}
if (g_moduleMgr[type].moduleMgr == NULL) {
return;
}
ModuleMgrDestroy(g_moduleMgr[type].moduleMgr);
g_moduleMgr[type].moduleMgr = NULL;
}
int AppSpawnLoadAutoRunModules(int type)
{
if (type >= MODULE_MAX) {
return -1;
}
if (g_moduleMgr[type].moduleMgr != NULL) {
return 0;
}
APPSPAWN_LOGI("AppSpawnLoadAutoRunModules: %{public}d moduleName: %{public}s", type, g_moduleMgr[type].moduleName);
#ifndef APPSPAWN_TEST
g_moduleMgr[type].moduleMgr = ModuleMgrScan(g_moduleMgr[type].moduleName);
return g_moduleMgr[type].moduleMgr == NULL ? -1 : 0;
#else
return 0;
#endif
}
HOOK_MGR *GetAppSpawnHookMgr(void)
{
if (g_appspawnHookMgr != NULL) {
return g_appspawnHookMgr;
}
g_appspawnHookMgr = HookMgrCreate("appspawn");
return g_appspawnHookMgr;
}
void DeleteAppSpawnHookMgr(void)
{
HookMgrDestroy(g_appspawnHookMgr);
g_appspawnHookMgr = NULL;
}
static int PreloadHookRun(const HOOK_INFO *hookInfo, void *executionContext)
{
AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
PreloadHook realHook = (PreloadHook)hookInfo->hookCookie;
return realHook((void *)arg->content);
}
static void PreHookExec(const HOOK_INFO *hookInfo, void *executionContext)
{
AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
AppSpawnMgr *spawnMgr = (AppSpawnMgr *)arg->content;
clock_gettime(CLOCK_MONOTONIC, &spawnMgr->perLoadStart);
APPSPAWN_LOGI("Hook stage: %{public}d prio: %{public}d start", hookInfo->stage, hookInfo->prio);
}
static void PostHookExec(const HOOK_INFO *hookInfo, void *executionContext, int executionRetVal)
{
AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
AppSpawnMgr *spawnMgr = (AppSpawnMgr *)arg->content;
clock_gettime(CLOCK_MONOTONIC, &spawnMgr->perLoadEnd);
uint64_t diff = DiffTime(&spawnMgr->perLoadStart, &spawnMgr->perLoadEnd);
APPSPAWN_LOGI("Hook stage: %{public}d prio: %{public}d end time %{public}" PRId64 " ns result: %{public}d",
hookInfo->stage, hookInfo->prio, diff, executionRetVal);
}
int PreloadHookExecute(AppSpawnContent *content)
{
AppSpawnHookArg arg;
arg.content = content;
arg.client = NULL;
HOOK_EXEC_OPTIONS options;
options.flags = TRAVERSE_STOP_WHEN_ERROR;
options.preHook = PreHookExec;
options.postHook = PostHookExec;
int ret = HookMgrExecute(GetAppSpawnHookMgr(), STAGE_SERVER_PRELOAD, (void *)(&arg), &options);
APPSPAWN_LOGI("Execute hook [%{public}d] result %{public}d", STAGE_SERVER_PRELOAD, ret);
return ret == ERR_NO_HOOK_STAGE ? 0 : ret;
}
int AddPreloadHook(int prio, PreloadHook hook)
{
APPSPAWN_CHECK(hook != NULL, return APPSPAWN_ARG_INVALID, "Invalid hook");
HOOK_INFO info;
info.stage = STAGE_SERVER_PRELOAD;
info.prio = prio;
info.hook = PreloadHookRun;
info.hookCookie = (void *)hook;
APPSPAWN_LOGI("AddPreloadHook prio: %{public}d", prio);
return HookMgrAddEx(GetAppSpawnHookMgr(), &info);
}
static int AppSpawnHookRun(const HOOK_INFO *hookInfo, void *executionContext)
{
AppSpawnForkArg *arg = (AppSpawnForkArg *)executionContext;
AppSpawnHook realHook = (AppSpawnHook)hookInfo->hookCookie;
return realHook((AppSpawnMgr *)arg->content, (AppSpawningCtx *)arg->client);
}
static void PreAppSpawnHookExec(const HOOK_INFO *hookInfo, void *executionContext)
{
AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
clock_gettime(CLOCK_MONOTONIC, &arg->tmStart);
APPSPAWN_LOGI("Hook stage: %{public}d prio: %{public}d start", hookInfo->stage, hookInfo->prio);
}
static void PostAppSpawnHookExec(const HOOK_INFO *hookInfo, void *executionContext, int executionRetVal)
{
AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
clock_gettime(CLOCK_MONOTONIC, &arg->tmEnd);
uint64_t diff = DiffTime(&arg->tmStart, &arg->tmEnd);
APPSPAWN_LOGI("Hook stage: %{public}d prio: %{public}d end time %{public}" PRId64 " ns result: %{public}d",
hookInfo->stage, hookInfo->prio, diff, executionRetVal);
}
int AppSpawnHookExecute(AppSpawnHookStage stage, uint32_t flags, AppSpawnContent *content, AppSpawnClient *client)
{
APPSPAWN_LOGV("Execute hook [%{public}d] for app: %{public}s", stage, GetProcessName((AppSpawningCtx *)client));
AppSpawnHookArg forkArg;
forkArg.client = client;
forkArg.content = content;
HOOK_EXEC_OPTIONS options;
options.flags = flags; // TRAVERSE_STOP_WHEN_ERROR : 0;
options.preHook = PreAppSpawnHookExec;
options.postHook = PostAppSpawnHookExec;
int ret = HookMgrExecute(GetAppSpawnHookMgr(), stage, (void *)(&forkArg), &options);
return ret == ERR_NO_HOOK_STAGE ? 0 : ret;
}
int AppSpawnExecuteClearEnvHook(AppSpawnContent *content, AppSpawnClient *client)
{
return AppSpawnHookExecute(STAGE_CHILD_PRE_COLDBOOT, HOOK_STOP_WHEN_ERROR, content, client);
}
int AppSpawnExecuteSpawningHook(AppSpawnContent *content, AppSpawnClient *client)
{
return AppSpawnHookExecute(STAGE_CHILD_EXECUTE, HOOK_STOP_WHEN_ERROR, content, client);
}
int AppSpawnExecutePostReplyHook(AppSpawnContent *content, AppSpawnClient *client)
{
return AppSpawnHookExecute(STAGE_CHILD_POST_RELY, HOOK_STOP_WHEN_ERROR, content, client);
}
int AppSpawnExecutePreReplyHook(AppSpawnContent *content, AppSpawnClient *client)
{
return AppSpawnHookExecute(STAGE_CHILD_PRE_RELY, HOOK_STOP_WHEN_ERROR, content, client);
}
void AppSpawnEnvClear(AppSpawnContent *content, AppSpawnClient *client)
{
(void)AppSpawnHookExecute(STAGE_CHILD_PRE_RUN, 0, content, client);
}
int AddAppSpawnHook(AppSpawnHookStage stage, int prio, AppSpawnHook hook)
{
APPSPAWN_CHECK(hook != NULL, return APPSPAWN_ARG_INVALID, "Invalid hook");
HOOK_INFO info;
info.stage = stage;
info.prio = prio;
info.hook = AppSpawnHookRun;
info.hookCookie = (void *)hook;
APPSPAWN_LOGI("AddAppSpawnHook stage: %{public}d prio: %{public}d", stage, prio);
return HookMgrAddEx(GetAppSpawnHookMgr(), &info);
}
int ProcessMgrHookExecute(AppSpawnHookStage stage, const AppSpawnContent *content,
const AppSpawnedProcessInfo *appInfo)
{
AppSpawnAppArg arg;
arg.appInfo = appInfo;
arg.content = content;
int ret = HookMgrExecute(GetAppSpawnHookMgr(), stage, (void *)(&arg), NULL);
return ret == ERR_NO_HOOK_STAGE ? 0 : ret;
}
static int ProcessMgrHookRun(const HOOK_INFO *hookInfo, void *executionContext)
{
AppSpawnAppArg *arg = (AppSpawnAppArg *)executionContext;
ProcessChangeHook realHook = (ProcessChangeHook)hookInfo->hookCookie;
return realHook((AppSpawnMgr *)arg->content, arg->appInfo);
}
int AddProcessMgrHook(AppSpawnHookStage stage, int prio, ProcessChangeHook hook)
{
APPSPAWN_CHECK(hook != NULL, return APPSPAWN_ARG_INVALID, "Invalid hook");
HOOK_INFO info;
info.stage = stage;
info.prio = prio;
info.hook = ProcessMgrHookRun;
info.hookCookie = hook;
return HookMgrAddEx(GetAppSpawnHookMgr(), &info);
}
void RegChildLooper(struct AppSpawnContent *content, ChildLoop loop)
{
APPSPAWN_CHECK(content != NULL && loop != NULL, return, "Invalid content for RegChildLooper");
content->runChildProcessor = loop;
}

View File

@ -0,0 +1,58 @@
/*
* 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.
*/
#ifndef APPSPAWN_MODULE_MGR_H
#define APPSPAWN_MODULE_MGR_H
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "hookmgr.h"
#include "modulemgr.h"
#include "appspawn_hook.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HOOK_STOP_WHEN_ERROR 0x2
typedef enum {
MODULE_DEFAULT,
MODULE_APPSPAWN,
MODULE_NWEBSPAWN,
MODULE_COMMON,
MODULE_MAX
} AppSpawnModuleType;
typedef struct {
AppSpawnContent *content;
AppSpawnClient *client;
struct timespec tmStart;
struct timespec tmEnd;
} AppSpawnHookArg;
int AppSpawnModuleMgrInstall(const char *mgrName);
int AppSpawnLoadAutoRunModules(int type);
void AppSpawnModuleMgrUnInstall(int type);
void DeleteAppSpawnHookMgr(void);
int PreloadHookExecute(AppSpawnContent *content);
int ProcessMgrHookExecute(AppSpawnHookStage stage, const AppSpawnContent *content, const AppSpawnedProcessInfo *appInfo);
int AppSpawnHookExecute(AppSpawnHookStage stage, uint32_t flags, AppSpawnContent *content, AppSpawnClient *client);
#ifdef __cplusplus
}
#endif
#endif // APPSPAWN_MODULE_MGR_H

53
modules/nweb_adapter/BUILD.gn Executable file
View File

@ -0,0 +1,53 @@
# 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.
import("//base/startup/appspawn/appspawn.gni")
import("//build/ohos.gni")
ohos_shared_library("appspawn_nweb") {
sources = [ "nwebspawn_adapter.cpp" ]
include_dirs = [
".",
"${appspawn_path}/common",
"${appspawn_path}/standard",
]
defines = []
cflags = []
deps = [ "${appspawn_path}/modules/module_engine:libappspawn_module_engine" ]
if (target_cpu == "arm64") {
defines += [ "webview_arm64" ]
}
if (target_cpu == "x86_64") {
defines += [ "webview_x86_64" ]
}
external_deps = [
"c_utils:utils",
"hilog:libhilog",
"init:libbegetutil",
]
if (build_seccomp) {
cflags += [ "-DWITH_SECCOMP" ]
external_deps += [ "init:seccomp" ]
}
subsystem_name = "${subsystem_name}"
part_name = "${part_name}"
install_enable = true
if (target_cpu == "arm64" || target_cpu == "x86_64" ||
target_cpu == "riscv64") {
module_install_dir = "lib64/appspawn/nwebspawn"
} else {
module_install_dir = "lib/appspawn/nwebspawn"
}
}

View File

@ -0,0 +1,133 @@
/*
* 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 <algorithm>
#include <cerrno>
#include <ctime>
#include <dlfcn.h>
#include <map>
#include <mutex>
#include <string>
#ifdef __MUSL__
#include <cerrno>
#include <dlfcn_ext.h>
#include <sys/mman.h>
#endif
#include "appspawn_hook.h"
#ifdef WITH_SECCOMP
#include "seccomp_policy.h"
#endif
namespace {
#if defined(webview_arm64)
const std::string NWEB_HAP_LIB_PATH = "/data/storage/el1/bundle/nweb/libs/arm64";
#elif defined(webview_x86_64)
const std::string NWEB_HAP_LIB_PATH = "/data/storage/el1/bundle/nweb/libs/x86_64";
#else
const std::string NWEB_HAP_LIB_PATH = "/data/storage/el1/bundle/nweb/libs/arm";
#endif
} // namespace
static bool SetSeccompPolicyForRenderer(void *nwebRenderHandle)
{
#ifdef WITH_SECCOMP
if (IsEnableSeccomp()) {
using SeccompFuncType = bool (*)(void);
SeccompFuncType funcSetRendererSeccompPolicy =
reinterpret_cast<SeccompFuncType>(dlsym(nwebRenderHandle, "SetRendererSeccompPolicy"));
if (funcSetRendererSeccompPolicy != nullptr && funcSetRendererSeccompPolicy()) {
return true;
}
APPSPAWN_LOGE("SetRendererSeccompPolicy dlsym errno: %{public}d", errno);
return false;
}
#endif
return true;
}
static int RunChildProcessor(AppSpawnContent *content, AppSpawnClient *client)
{
uint32_t len = 0;
char *renderCmd = reinterpret_cast<char *>(GetAppPropertyExt(
reinterpret_cast<AppSpawningCtx *>(client), MSG_EXT_NAME_RENDER_CMD, &len));
if (renderCmd == nullptr) {
return -1;
}
std::string renderStr(renderCmd);
void *webEngineHandle = nullptr;
void *nwebRenderHandle = nullptr;
#ifdef __MUSL__
Dl_namespace dlns;
dlns_init(&dlns, "nweb_ns");
dlns_create(&dlns, NWEB_HAP_LIB_PATH.c_str());
// preload libweb_engine
webEngineHandle = dlopen_ns(&dlns, "libweb_engine.so", RTLD_NOW | RTLD_GLOBAL);
// load libnweb_render
nwebRenderHandle = dlopen_ns(&dlns, "libnweb_render.so", RTLD_NOW | RTLD_GLOBAL);
#else
// preload libweb_engine
const std::string engineLibDir = NWEB_HAP_LIB_PATH + "/libweb_engine.so";
webEngineHandle = dlopen(engineLibDir.c_str(), RTLD_NOW | RTLD_GLOBAL);
// load libnweb_render
const std::string renderLibDir = NWEB_HAP_LIB_PATH + "/libnweb_render.so";
nwebRenderHandle = dlopen(renderLibDir.c_str(), RTLD_NOW | RTLD_GLOBAL);
#endif
if (webEngineHandle == nullptr) {
APPSPAWN_LOGE("Fail to dlopen libweb_engine.so, errno: %{public}d", errno);
}
if (nwebRenderHandle == nullptr) {
APPSPAWN_LOGE("Fail to dlopen libnweb_render.so, errno: %{public}d", errno);
return -1;
}
if (!SetSeccompPolicyForRenderer(nwebRenderHandle)) {
return -1;
}
using FuncType = void (*)(const char *cmd);
FuncType funcNWebRenderMain = reinterpret_cast<FuncType>(dlsym(nwebRenderHandle, "NWebRenderMain"));
if (funcNWebRenderMain == nullptr) {
APPSPAWN_LOGE("webviewspawn dlsym errno: %{public}d", errno);
return -1;
}
AppSpawnEnvClear(content, client);
funcNWebRenderMain(renderStr.c_str());
APPSPAWN_LOGI("RunChildProcessorNweb %{public}s", renderStr.c_str());
return 0;
}
static int PreLoadNwebSpawn(AppSpawnMgr *content)
{
APPSPAWN_LOGI("PreLoadNwebSpawn %{public}d", IsNWebSpawnMode(content));
if (!IsNWebSpawnMode(content)) {
return 0;
}
// register
RegChildLooper(&content->content, RunChildProcessor);
return 0;
}
MODULE_CONSTRUCTOR(void)
{
APPSPAWN_LOGI("Load nweb module ...");
AddPreloadHook(HOOK_PRIO_HIGHEST, PreLoadNwebSpawn);
}

58
modules/sandbox/BUILD.gn Executable file
View File

@ -0,0 +1,58 @@
# 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.
import("//base/startup/appspawn/appspawn.gni")
import("//build/ohos.gni")
ohos_shared_library("appspawn_sandbox") {
sources = [
"appspawn_mount_template.c",
"appspawn_permission.c",
"appspawn_sandbox.c",
"sandbox_cfgvar.c",
"sandbox_expand.c",
"sandbox_load.c",
"sandbox_manager.c",
]
include_dirs = [
".",
"${appspawn_path}/common",
"${appspawn_path}/standard",
"${appspawn_path}/modules//module_engine//include",
]
configs = [ "${appspawn_path}:appspawn_config" ]
defines = []
deps = [
"${appspawn_path}/modules/module_engine:libappspawn_module_engine",
"${appspawn_path}/util:libappspawn_util",
"//third_party/cJSON:cjson",
]
external_deps = [
"c_utils:utils",
"hilog:libhilog",
"init:libbegetutil",
]
subsystem_name = "${subsystem_name}"
part_name = "${part_name}"
install_enable = true
if (target_cpu == "arm64" || target_cpu == "x86_64" ||
target_cpu == "riscv64") {
defines += [ "APPSPAWN_64" ]
module_install_dir = "lib64/appspawn/common"
} else {
module_install_dir = "lib/appspawn/common"
}
}

View File

@ -0,0 +1,187 @@
/*
* 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 <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include "appspawn_sandbox.h"
#include "appspawn_utils.h"
#include "securec.h"
/*
fs-type flags options
default MS_BIND MS_REC
rdonly MS_NODEV MS_RDONLY
epfs epfs MS_NODEV
dac_override MS_NODEV MS_RDONLY "support_overwrite=1"
const.filemanager.full_mount.enable fuse fuse MS_NOSUID MS_NODEV MS_NOEXEC MS_NOATIME MS_LAZYTIME
dlp_fuse fuse MS_NOSUID MS_NODEV MS_NOEXEC MS_NOATIME MS_LAZYTIME dlpmanager管理应用专用的挂载参数
shared MS_BIND MS_REC root
namespace上是MS_SHARED方式挂载
*/
/**
"dac-override-sensitive": "true", false使使
"sandbox-flags-customized": [ "MS_NODEV", "MS_RDONLY" ],
"fs-type": "sharefs",
"options": "support_overwrite=1"
*/
#define FUSE_MOUNT_FLAGS (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME | MS_LAZYTIME)
#define FUSE_MOUNT_OPTIONS \
"fd=%d, rootmode=40000,user_id=%d,group_id=%d,allow_other," \
"context=\"u:object_r:dlp_fuse_file:s0\", fscontext=u:object_r:dlp_fuse_file:s0"
static const MountArgTemplate DEF_MOUNT_ARG_TMP[MOUNT_TMP_MAX] = {
{"default", MOUNT_TMP_DEFAULT, NULL, MS_BIND | MS_REC, NULL, MS_SLAVE},
{"rdonly", MOUNT_TMP_RDONLY, NULL, MS_NODEV | MS_RDONLY, NULL, MS_SLAVE},
{"epfs", MOUNT_TMP_EPFS, "epfs", MS_NODEV, NULL, MS_SLAVE},
{"dac_override", MOUNT_TMP_DAC_OVERRIDE, "sharefs", MS_NODEV | MS_RDONLY, "support_overwrite=1", MS_SLAVE},
{"fuse", MOUNT_TMP_FUSE, "fuse", FUSE_MOUNT_FLAGS, FUSE_MOUNT_OPTIONS, MS_SHARED},
{"dlp_fuse", MOUNT_TMP_DLP_FUSE, "fuse", FUSE_MOUNT_FLAGS, FUSE_MOUNT_OPTIONS, MS_SHARED},
{"shared", MOUNT_TMP_SHRED, NULL, MS_BIND | MS_REC, NULL, MS_SHARED},
};
static const SandboxFlagInfo MOUNT_FLAGS_MAP[] = {
{"rec", MS_REC}, {"MS_REC", MS_REC},
{"bind", MS_BIND}, {"MS_BIND", MS_BIND}, {"move", MS_MOVE}, {"MS_MOVE", MS_MOVE},
{"slave", MS_SLAVE}, {"MS_SLAVE", MS_SLAVE}, {"rdonly", MS_RDONLY}, {"MS_RDONLY", MS_RDONLY},
{"shared", MS_SHARED}, {"MS_SHARED", MS_SHARED}, {"unbindable", MS_UNBINDABLE},
{"MS_UNBINDABLE", MS_UNBINDABLE}, {"remount", MS_REMOUNT}, {"MS_REMOUNT", MS_REMOUNT},
{"nosuid", MS_NOSUID}, {"MS_NOSUID", MS_NOSUID}, {"nodev", MS_NODEV}, {"MS_NODEV", MS_NODEV},
{"noexec", MS_NOEXEC}, {"MS_NOEXEC", MS_NOEXEC}, {"noatime", MS_NOATIME}, {"MS_NOATIME", MS_NOATIME},
{"lazytime", MS_LAZYTIME}, {"MS_LAZYTIME", MS_LAZYTIME}
};
static const SandboxFlagInfo PATH_MODE_MAP[] = {
{"S_IRUSR", S_IRUSR}, {"S_IWUSR", S_IWUSR}, {"S_IXUSR", S_IXUSR},
{"S_IRGRP", S_IRGRP}, {"S_IWGRP", S_IWGRP}, {"S_IXGRP", S_IXGRP},
{"S_IROTH", S_IROTH}, {"S_IWOTH", S_IWOTH}, {"S_IXOTH", S_IXOTH},
{"S_IRWXU", S_IRWXU}, {"S_IRWXG", S_IRWXG}, {"S_IRWXO", S_IRWXO}
};
uint32_t GetMountCategory(const char *name)
{
APPSPAWN_CHECK_ONLY_EXPER(name != NULL, return MOUNT_TMP_DEFAULT);
uint32_t count = ARRAY_LENGTH(DEF_MOUNT_ARG_TMP);
for (uint32_t i = 0; i < count; i++) {
if (strcmp(DEF_MOUNT_ARG_TMP[i].name, name) == 0) {
return DEF_MOUNT_ARG_TMP[i].category;
}
}
return MOUNT_TMP_DEFAULT;
}
const MountArgTemplate *GetMountArgTemplate(uint32_t category)
{
uint32_t count = ARRAY_LENGTH(DEF_MOUNT_ARG_TMP);
if (category > count) {
return NULL;
}
for (uint32_t i = 0; i < count; i++) {
if (DEF_MOUNT_ARG_TMP[i].category == category) {
return &DEF_MOUNT_ARG_TMP[i];
}
}
return NULL;
}
const SandboxFlagInfo *GetSandboxFlagInfo(const char *key, const SandboxFlagInfo *flagsInfos, uint32_t count)
{
APPSPAWN_CHECK_ONLY_EXPER(key != NULL, return NULL);
APPSPAWN_CHECK_ONLY_EXPER(flagsInfos != NULL, return NULL);
for (uint32_t i = 0; i < count; i++) {
if (strcmp(flagsInfos[i].name, key) == 0) {
return &flagsInfos[i];
}
}
return NULL;
}
int GetPathMode(const char *name)
{
APPSPAWN_CHECK_ONLY_EXPER(name != NULL, return 0);
const SandboxFlagInfo *info = GetSandboxFlagInfo(name, PATH_MODE_MAP, ARRAY_LENGTH(PATH_MODE_MAP));
if (info != NULL) {
return info->flags;
}
return 0;
}
static void DumpSandboxFlags(char *buffer, uint32_t bufferSize, unsigned long flags,
const SandboxFlagInfo *flagsInfos, uint32_t count)
{
bool first = true;
size_t currLen = 0;
int len = 0;
unsigned long tmp = flags;
for (uint32_t i = 0; i < count; i++) {
if ((flagsInfos[i].flags & tmp) == 0) {
continue;
}
tmp &= ~(flagsInfos[i].flags);
if (!first) {
len = sprintf_s(buffer + currLen, bufferSize - currLen - 1, " %s ", "|");
APPSPAWN_CHECK_ONLY_EXPER(len > 0, return);
currLen += len;
}
first = false;
len = sprintf_s(buffer + currLen, bufferSize - currLen, "%s", flagsInfos[i].name);
APPSPAWN_CHECK_ONLY_EXPER(len > 0, return);
currLen += len;
}
}
static void DumpMode(const char *info, mode_t mode)
{
APPSPAWN_CHECK_ONLY_EXPER(info != NULL, return);
char buffer[64] = {0}; // 64 to show flags
DumpSandboxFlags(buffer, sizeof(buffer), mode, PATH_MODE_MAP, ARRAY_LENGTH(PATH_MODE_MAP));
APPSPAPWN_DUMP("%{public}s[0x%{public}x] %{public}s", info, (uint32_t)(mode), buffer);
}
static void DumpMountFlags(const char *info, unsigned long mountFlags)
{
APPSPAWN_CHECK_ONLY_EXPER(info != NULL, return);
char buffer[128] = {0}; // 64 to show flags
DumpSandboxFlags(buffer, sizeof(buffer), mountFlags, MOUNT_FLAGS_MAP, ARRAY_LENGTH(MOUNT_FLAGS_MAP));
APPSPAPWN_DUMP("%{public}s[0x%{public}x] %{public}s", info, (uint32_t)(mountFlags), buffer);
}
void DumpMountPathMountNode(const PathMountNode *pathNode)
{
const MountArgTemplate *tmp = GetMountArgTemplate(pathNode->category);
if (tmp == NULL) {
return;
}
APPSPAPWN_DUMP(" sandbox node category: %{public}u(%{public}s)", tmp->category, tmp->name);
DumpMountFlags(" sandbox node mountFlags: ", tmp->mountFlags);
APPSPAPWN_DUMP(" sandbox node mountSharedFlag: %{public}s",
tmp->mountSharedFlag == MS_SLAVE ? "MS_SLAVE" : "MS_SHARED");
APPSPAPWN_DUMP(" sandbox node options: %{public}s", tmp->options ? tmp->options : "null");
APPSPAPWN_DUMP(" sandbox node fsType: %{public}s", tmp->fsType ? tmp->fsType : "null");
DumpMode(" sandbox node destMode: ", pathNode->destMode);
APPSPAPWN_DUMP(" sandbox node mountSharedFlag: %{public}s",
pathNode->mountSharedFlag ? "MS_SHARED" : "MS_SLAVE");
}

View File

@ -13,10 +13,14 @@
* limitations under the License.
*/
#include "appspawn_permission.h"
#include "appspawn_utils.h"
#ifdef APPSPAWN_CLIENT
#include "appspawn_mount_permission.h"
#else
#include "appspawn_sandbox.h"
#endif
#include "appspawn_msg.h"
#include "appspawn_utils.h"
#include "securec.h"
#include "interfaces/innerkits_new/permission/appspawn_mount_permission.h"
static int PermissionNodeCompareIndex(ListNode *node, void *data)
{
@ -27,18 +31,35 @@ static int PermissionNodeCompareIndex(ListNode *node, void *data)
static int PermissionNodeCompareName(ListNode *node, void *data)
{
SandboxPermissionNode *permissionNode = (SandboxPermissionNode *)ListEntry(node, SandboxMountNode, node);
#ifdef APPSPAWN_CLIENT
return strcmp(permissionNode->name, (char *)data);
#else
return strcmp(permissionNode->section.name, (char *)data);
#endif
}
static int PermissionNodeCompareProc(ListNode *node, ListNode *newNode)
{
SandboxPermissionNode *permissionNode = (SandboxPermissionNode *)ListEntry(node, SandboxMountNode, node);
SandboxPermissionNode *newPermissionNode = (SandboxPermissionNode *)ListEntry(newNode, SandboxMountNode, node);
#ifdef APPSPAWN_CLIENT
return strcmp(permissionNode->name, newPermissionNode->name);
#else
return strcmp(permissionNode->section.name, newPermissionNode->section.name);
#endif
}
int AddSandboxPermissionNode(const char *name, SandboxQueue *queue)
{
APPSPAWN_CHECK_ONLY_EXPER(name != NULL && queue != NULL, return APPSPAWN_ARG_INVALID);
#ifndef APPSPAWN_CLIENT
size_t len = sizeof(SandboxPermissionNode);
SandboxPermissionNode *node = (SandboxPermissionNode *)CreateSandboxSection(
name, len, SANDBOX_TAG_PERMISSION);
APPSPAWN_CHECK(node != NULL, return APPSPAWN_SYSTEM_ERROR, "Failed to create permission node");
node->permissionIndex = 0;
OH_ListAddWithOrder(&queue->front, &node->section.sandboxNode.node, PermissionNodeCompareProc);
#else
size_t len = APPSPAWN_ALIGN(strlen(name) + 1) + sizeof(SandboxPermissionNode);
SandboxPermissionNode *node = (SandboxPermissionNode *)calloc(1, len);
APPSPAWN_CHECK(node != NULL, return APPSPAWN_SYSTEM_ERROR, "Failed to create permission node");
@ -48,18 +69,25 @@ int AddSandboxPermissionNode(const char *name, SandboxQueue *queue)
APPSPAWN_CHECK(ret == 0, free(node);
return APPSPAWN_SYSTEM_ERROR, "Failed to copy name");
OH_ListAddWithOrder(&queue->front, &node->sandboxNode.node, PermissionNodeCompareProc);
#endif
return 0;
}
int32_t PermissionRenumber(SandboxQueue *queue)
{
APPSPAWN_CHECK_ONLY_EXPER(queue != NULL, return -1);
ListNode *node = queue->front.next;
int index = -1;
while (node != &queue->front) {
SandboxPermissionNode *permissionNode = (SandboxPermissionNode *)ListEntry(node, SandboxMountNode, node);
permissionNode->permissionIndex = ++index;
#ifdef APPSPAWN_CLIENT
APPSPAWN_LOGV("Permission index %{public}d name %{public}s",
permissionNode->permissionIndex, permissionNode->name);
#else
APPSPAWN_LOGV("Permission index %{public}d name %{public}s",
permissionNode->permissionIndex, permissionNode->section.name);
#endif
node = node->next;
}
return index + 1;
@ -85,6 +113,7 @@ const SandboxPermissionNode *GetPermissionNodeInQueueByIndex(SandboxQueue *queue
int32_t GetPermissionIndexInQueue(SandboxQueue *queue, const char *permission)
{
APPSPAWN_CHECK_ONLY_EXPER(queue != NULL && permission != NULL, return INVALID_PERMISSION_INDEX);
const SandboxPermissionNode *permissionNode = GetPermissionNodeInQueue(queue, permission);
return permissionNode == NULL ? INVALID_PERMISSION_INDEX : permissionNode->permissionIndex;
}

View File

@ -26,14 +26,15 @@ extern "C" {
#endif
#define APP_SANDBOX_FILE_NAME "/appdata-sandbox.json"
typedef struct TagSandboxSection SandboxQueue;
#define WEB_SANDBOX_FILE_NAME "/appdata-sandbox-nweb.json"
typedef struct TagSandboxQueue SandboxQueue;
typedef struct TagPermissionNode SandboxPermissionNode;
int32_t AddSandboxPermissionNode(const char *name, SandboxQueue *queue);
int32_t GetPermissionIndexInQueue(SandboxQueue *queue, const char *permission);
const SandboxPermissionNode *GetPermissionNodeInQueue(SandboxQueue *queue, const char *permission);
const SandboxPermissionNode *GetPermissionNodeInQueueByIndex(SandboxQueue *queue, int32_t index);
const SandboxPermissionNode *GetPermissionNode(const char *permission);
int32_t PermissionRenumber(SandboxQueue *queue);
#ifdef __cplusplus

View File

@ -0,0 +1,824 @@
/*
* 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 "appspawn_sandbox.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include "appspawn_msg.h"
#include "appspawn_utils.h"
#include "init_utils.h"
#include "parameter.h"
#include "securec.h"
static inline bool CheckSpawningMsgFlagSet(const SandboxContext *context, uint32_t index)
{
APPSPAWN_CHECK(context->message != NULL, return false, "Invalid property for type %{public}u", TLV_MSG_FLAGS);
return CheckAppSpawnMsgFlag(context->message, TLV_MSG_FLAGS, index);
}
static inline bool CheckSpawningPermissionFlagSet(const SandboxContext *context, uint32_t index)
{
APPSPAWN_CHECK(context != NULL && context->message != NULL,
return NULL, "Invalid property for type %{public}u", TLV_PERMISSION);
return CheckAppSpawnMsgFlag(context->message, TLV_PERMISSION, index);
}
static SandboxContext *g_sandboxContext = NULL;
SandboxContext *GetSandboxContext(void)
{
if (g_sandboxContext == NULL) {
SandboxContext *context = calloc(1, MAX_SANDBOX_BUFFER * MAX_BUFFER + sizeof(SandboxContext));
APPSPAWN_CHECK(context != NULL, return NULL, "Failed to get mem");
char *buffer = (char *)(context + 1);
for (int i = 0; i < MAX_BUFFER; i++) {
context->buffer[i].bufferLen = MAX_SANDBOX_BUFFER;
context->buffer[i].current = 0;
context->buffer[i].buffer = buffer + MAX_SANDBOX_BUFFER * i;
}
context->bundleName = NULL;
context->bundleHasWps = 0;
context->dlpBundle = 0;
context->appFullMountEnable = 0;
context->dlpUiExtType = 0;
context->sandboxSwitch = 1;
context->sandboxShared = false;
context->message = NULL;
context->rootPath = NULL;
context->sandboxPackagePath = NULL;
g_sandboxContext = context;
}
return g_sandboxContext;
}
void DeleteSandboxContext(SandboxContext *context)
{
APPSPAWN_CHECK_ONLY_EXPER(context != NULL, return);
if (context->rootPath) {
free(context->rootPath);
context->rootPath = NULL;
}
if (context->sandboxPackagePath) {
free(context->sandboxPackagePath);
context->sandboxPackagePath = NULL;
}
if (context == g_sandboxContext) {
g_sandboxContext = NULL;
}
free(context);
}
static int InitSandboxContext(SandboxContext *context,
const AppSpawnSandboxCfg *sandbox, const AppSpawningCtx *property, int nwebspawn)
{
AppSpawnMsgFlags *msgFlags = (AppSpawnMsgFlags *)GetAppProperty(property, TLV_MSG_FLAGS);
APPSPAWN_CHECK(msgFlags != NULL, return APPSPAWN_TLV_NONE,
"No msg flags in msg %{public}s", GetProcessName(property));
context->nwebspawn = nwebspawn;
context->bundleName = GetBundleName(property);
context->bundleHasWps = strstr(context->bundleName, "wps") != NULL;
context->dlpBundle = strstr(context->bundleName, "com.ohos.dlpmanager") != NULL;
context->appFullMountEnable = sandbox->appFullMountEnable;
context->dlpUiExtType = strstr(GetProcessName(property), "sys/commonUI") != NULL;
context->sandboxSwitch = 1;
context->sandboxShared = false;
SandboxPackageNameNode *packageNode = (SandboxPackageNameNode *)GetSandboxSection(
&sandbox->packageNameQueue, context->bundleName);
if (packageNode) {
context->sandboxShared = packageNode->section.sandboxShared;
}
context->message = property->message;
return 0;
}
static VarExtraData *GetVarExtraData(const SandboxContext *context, const SandboxSection *section)
{
static VarExtraData extraData;
(void)memset_s(&extraData, sizeof(extraData), 0, sizeof(extraData));
extraData.sandboxTag = GetSectionType(section);
if (extraData.sandboxTag == SANDBOX_TAG_NAME_GROUP) {
SandboxNameGroupNode *groupNode = (SandboxNameGroupNode *)section;
extraData.data.depNode = groupNode->depNode;
}
return &extraData;
}
static int BuildPackagePath(SandboxContext *sandboxContext, const AppSpawnSandboxCfg *sandbox)
{
const char *packagePath = GetSandboxRealVar(sandboxContext, BUFFER_FOR_SOURCE, sandbox->rootPath, NULL, NULL);
if (packagePath) {
sandboxContext->sandboxPackagePath = strdup(packagePath);
}
APPSPAWN_CHECK(sandboxContext->sandboxPackagePath != NULL,
return APPSPAWN_SYSTEM_ERROR, "Failed to format path app: %{public}s", sandboxContext->bundleName);
return 0;
}
static int BuildRootPath(SandboxContext *context, const AppSpawnSandboxCfg *sandbox, const SandboxSection *section)
{
const char *rootPath = GetSandboxRealVar(context, BUFFER_FOR_SOURCE, sandbox->rootPath, NULL, NULL);
if (rootPath) {
context->rootPath = strdup(rootPath);
}
APPSPAWN_CHECK(context->rootPath != NULL, return -1,
"Failed to build root path app: %{public}s", context->bundleName);
return 0;
}
static uint32_t GetMountArgs(const SandboxContext *context, const PathMountNode *sandboxNode, MountArg *args)
{
uint32_t category = sandboxNode->category;
if ((category == MOUNT_TMP_DLP_FUSE) && !context->appFullMountEnable) { // use default
category = MOUNT_TMP_DEFAULT;
}
const MountArgTemplate *tmp = GetMountArgTemplate(category);
if (tmp == 0) {
return MOUNT_TMP_DEFAULT;
}
args->mountFlags = tmp->mountFlags;
args->fsType = tmp->fsType;
args->options = tmp->options;
args->mountSharedFlag = (sandboxNode->mountSharedFlag) ? MS_SHARED : tmp->mountSharedFlag;
return category;
}
static int CheckSandboxMountNode(const SandboxContext *context,
const SandboxSection *section, const PathMountNode *sandboxNode)
{
if (sandboxNode->source == NULL || sandboxNode->target == NULL) {
APPSPAWN_LOGW("Invalid mount config section %{public}s", section->name);
return 0;
}
// special handle wps and don't use /data/app/xxx/<Package> config
if (GetSectionType(section) == SANDBOX_TAG_SPAWN_FLAGS) { // flags-point
if (context->bundleHasWps &&
(strstr(sandboxNode->source, "/data/app") != NULL) &&
(strstr(sandboxNode->source, "/base") != NULL || strstr(sandboxNode->source, "/database") != NULL) &&
(strstr(sandboxNode->source, PARAMETER_PACKAGE_NAME) != NULL)) {
APPSPAWN_LOGW("Invalid mount source %{public}s section %{public}s",
sandboxNode->source, section->name);
return 0;
}
}
// check apl
AppSpawnMsgDomainInfo *msgDomainInfo = (AppSpawnMsgDomainInfo *)GetSpawningMsgInfo(context, TLV_DOMAIN_INFO);
if (msgDomainInfo != NULL && sandboxNode->appAplName != NULL) {
if (!strcmp(sandboxNode->appAplName, msgDomainInfo->apl)) {
APPSPAWN_LOGW("Invalid mount app apl %{public}s %{public}s section %{public}s",
sandboxNode->appAplName, msgDomainInfo->apl, section->name);
return 0;
}
}
return 1;
}
static int32_t DoDlpAppMountStrategy(const SandboxContext *context, const MountArg *args)
{
AppSpawnMsgDacInfo *info = (AppSpawnMsgDacInfo *)GetSpawningMsgInfo(context, TLV_DAC_INFO);
APPSPAWN_CHECK(info != NULL, return APPSPAWN_TLV_NONE,
"No tlv %{public}d in msg %{public}s", TLV_DAC_INFO, context->bundleName);
// umount fuse path, make sure that sandbox path is not a mount point
umount2(args->destinationPath, MNT_DETACH);
int fd = open("/dev/fuse", O_RDWR);
APPSPAWN_CHECK(fd != -1, return -EINVAL,
"open /dev/fuse failed, errno: %{public}d sandbox path %{public}s", errno, args->destinationPath);
char options[FUSE_OPTIONS_MAX_LEN];
(void)sprintf_s(options, sizeof(options), "fd=%d,"
"rootmode=40000,user_id=%d,group_id=%d,allow_other,"
"context=\"u:object_r:dlp_fuse_file:s0\","
"fscontext=u:object_r:dlp_fuse_file:s0", fd, info->uid, info->gid);
// To make sure destinationPath exist
CreateSandboxDir(args->destinationPath, FILE_MODE);
MountArg mountArg = {args->originPath, args->destinationPath, args->fsType, args->mountFlags, options, MS_SHARED};
int ret = SandboxMountPath(&mountArg);
if (ret != 0) {
close(fd);
return -1;
}
/* close DLP_FUSE_FD and dup FD to it */
close(DLP_FUSE_FD);
ret = dup2(fd, DLP_FUSE_FD);
APPSPAWN_CHECK_ONLY_LOG(ret != -1, "dup fuse fd %{public}d failed, errno: %{public}d", fd, errno);
return 0;
}
static void CheckAndCreateSandboxFile(const char *file)
{
if (access(file, F_OK) == 0) {
APPSPAWN_LOGI("file %{public}s already exist", file);
return;
}
MakeDirRec(file, FILE_MODE, 0);
int fd = open(file, O_CREAT, FILE_MODE);
if (fd < 0) {
APPSPAWN_LOGW("failed create %{public}s, err=%{public}d", file, errno);
} else {
close(fd);
}
return;
}
static int DoSandboxPathNodeMount(const SandboxContext *context,
const SandboxSection *section, const PathMountNode *sandboxNode, int operation)
{
if (CheckSandboxMountNode(context, section, sandboxNode) == 0) {
return 0;
}
MountArg args = {};
uint32_t category = GetMountArgs(context, sandboxNode, &args);
args.originPath = GetSandboxRealVar(context, BUFFER_FOR_SOURCE, sandboxNode->source, NULL, NULL);
// only destinationPath
VarExtraData *extraData = GetVarExtraData(context, section);
args.destinationPath = GetSandboxRealVar(context,
BUFFER_FOR_TARGET, sandboxNode->target, context->rootPath, extraData);
APPSPAWN_CHECK(args.originPath != NULL && args.destinationPath != NULL,
return APPSPAWN_ARG_INVALID, "Invalid path %{public}s %{public}s", args.originPath, args.destinationPath);
if (sandboxNode->sandboxNode.type == SANDBOX_TAG_MOUNT_FILE) {
CheckAndCreateSandboxFile(args.destinationPath);
} else {
CreateSandboxDir(args.destinationPath, FILE_MODE);
}
APPSPAWN_LOGV("Bind mount category %{public}u op %{public}x \n "
"mount arg: %{public}s %{public}s %{public}x %{public}s \n %{public}s => %{public}s",
category, operation, args.fsType, args.mountSharedFlag == MS_SHARED ? "MS_SHARED" : "MS_SLAVE",
(uint32_t)args.mountFlags, args.options, args.originPath, args.destinationPath);
if ((operation & MOUNT_PATH_OP_UNMOUNT) == MOUNT_PATH_OP_UNMOUNT) {
// unmount this deps
APPSPAWN_LOGV("DoSandboxPathNodeMount umount2 %{public}s", args.destinationPath);
umount2(args.destinationPath, MNT_DETACH);
}
int ret = 0;
if (category == MOUNT_TMP_DLP_FUSE || category == MOUNT_TMP_DLP_FUSE) {
ret = DoDlpAppMountStrategy(context, &args);
} else {
ret = SandboxMountPath(&args);
}
if (ret != 0 && sandboxNode->checkErrorFlag) {
APPSPAWN_LOGE("Failed to mount config, section: %{public}s result: %{public}d category: %{public}d",
section->name, ret, category);
return ret;
}
if (sandboxNode->destMode != 0) {
chmod(context->rootPath, sandboxNode->destMode);
}
return 0;
}
static int DoSandboxPathSymLink(const SandboxContext *context,
const SandboxSection *section, const SymbolLinkNode *sandboxNode)
{
// Check the validity of the symlink configuration
if (sandboxNode->linkName == NULL || sandboxNode->target == NULL) {
APPSPAWN_LOGW("Invalid symlink config, section %{public}s", section->name);
return 0;
}
const char *target = GetSandboxRealVar(context, BUFFER_FOR_SOURCE, sandboxNode->target, NULL, NULL);
const char *linkName = GetSandboxRealVar(context, BUFFER_FOR_TARGET,
sandboxNode->linkName, context->rootPath, NULL);
APPSPAWN_LOGV("symlink, from %{public}s to %{public}s", target, linkName);
int ret = symlink(target, linkName);
if (ret && errno != EEXIST) {
if (sandboxNode->checkErrorFlag) {
APPSPAWN_LOGE("symlink failed, errno: %{public}d link info %{public}s %{public}s",
errno, sandboxNode->target, sandboxNode->linkName);
return errno;
}
APPSPAWN_LOGV("symlink failed, errno: %{public}d link info %{public}s %{public}s",
errno, sandboxNode->target, sandboxNode->linkName);
}
if (sandboxNode->destMode != 0) {
chmod(context->rootPath, sandboxNode->destMode);
}
return 0;
}
static int DoSandboxNodeMount(const SandboxContext *context, const SandboxSection *section, int operation)
{
APPSPAWN_LOGV("DoSandboxNodeMount section %{public}s operation %{public}x", section->name, operation);
ListNode *node = section->front.next;
while (node != &section->front) {
int ret = 0;
SandboxMountNode *sandboxNode = (SandboxMountNode *)ListEntry(node, SandboxMountNode, node);
switch (sandboxNode->type) {
case SANDBOX_TAG_MOUNT_PATH:
case SANDBOX_TAG_MOUNT_FILE:
ret = DoSandboxPathNodeMount(context, section, (PathMountNode *)sandboxNode, operation);
break;
case SANDBOX_TAG_SYMLINK:
if ((operation & MOUNT_PATH_OP_NO_SYMLINK) == MOUNT_PATH_OP_NO_SYMLINK) {
break;
}
ret = DoSandboxPathSymLink(context, section, (SymbolLinkNode *)sandboxNode);
break;
default:
break;
}
if (ret != 0) {
return ret;
}
node = node->next;
}
return 0;
}
static int UpdateMountPathDepsPath(const SandboxContext *context, SandboxNameGroupNode *groupNode)
{
PathMountNode *depNode = groupNode->depNode;
const char *srcPath = GetSandboxRealVar(context, BUFFER_FOR_SOURCE, depNode->source, NULL, NULL);
const char *sandboxPath = GetSandboxRealVar(context, BUFFER_FOR_TARGET, depNode->target, NULL, NULL);
if (srcPath == NULL || sandboxPath == NULL) {
APPSPAWN_LOGE("Failed to get real path %{public}s ", groupNode->section.name);
return APPSPAWN_SANDBOX_MOUNT_FAIL;
}
free(depNode->source);
depNode->source = strdup(srcPath);
free(depNode->target);
depNode->target = strdup(sandboxPath);
if (depNode->source == NULL || depNode->target == NULL) {
APPSPAWN_LOGE("Failed to get real path %{public}s ", groupNode->section.name);
if (depNode->source) {
free(depNode->source);
depNode->source = NULL;
}
if (depNode->target) {
free(depNode->target);
depNode->target = NULL;
}
return APPSPAWN_SANDBOX_MOUNT_FAIL;
}
return 0;
}
static bool CheckAndCreateDepPath(const SandboxContext *context, const SandboxNameGroupNode *sandboxNode)
{
if (sandboxNode->mountMode != MOUNT_MODE_NOT_EXIST) {
return false;
}
PathMountNode *mountNode = (PathMountNode *)GetFirstSandboxMountNode(&sandboxNode->section);
if (mountNode == NULL) {
return false;
}
const char *srcPath = GetSandboxRealVar(context, BUFFER_FOR_SOURCE, mountNode->source, NULL, NULL);
if (srcPath == NULL) {
return false;
}
APPSPAWN_LOGV("Mount depended to check src: %{public}s", srcPath);
if (access(srcPath, F_OK) == 0) {
return true;
}
// 这里已经是实际路径了,不需要转化
APPSPAWN_LOGV("Mount depended source: %{public}s", sandboxNode->depNode->source);
CreateSandboxDir(sandboxNode->depNode->source, FILE_MODE);
return false;
}
static int MountNameGroupPath(const SandboxContext *context,
const AppSpawnSandboxCfg *sandbox, const SandboxNameGroupNode *sandboxNode)
{
int ret = 0;
APPSPAWN_LOGV("Set name-group: '%{public}s' root path: '%{public}s' global %{public}s",
sandboxNode->section.name, context->rootPath, sandbox->rootPath);
ret = DoSandboxNodeMount(context, &sandboxNode->section, 0);
APPSPAWN_CHECK(ret == 0, return ret,
"Mount name group %{public}s fail result: %{public}d", sandboxNode->section.name, ret);
return ret;
}
static int MountSandboxConfig(const SandboxContext *context,
const AppSpawnSandboxCfg *sandbox, const SandboxSection *section, int operation)
{
// if sandbox switch is off, don't do symlink work again
operation |= !(context->sandboxSwitch && sandbox->topSandboxSwitch) ? MOUNT_PATH_OP_NO_SYMLINK : 0;
APPSPAWN_LOGV("Set config section: %{public}s root path: '%{public}s' symlinkDo: %{public}x",
section->name, context->rootPath, operation);
int ret = DoSandboxNodeMount(context, section, operation);
APPSPAWN_CHECK(ret == 0, return ret,
"Mount sandbox config fail result: %{public}d, app: %{public}s", ret, context->bundleName);
if (section->nameGroups == NULL) {
return 0;
}
for (uint32_t i = 0; i < section->number; i++) {
if (section->nameGroups[i] == NULL) {
continue;
}
SandboxNameGroupNode *groupNode = (SandboxNameGroupNode *)section->nameGroups[i];
MountNameGroupPath(context, sandbox, groupNode);
}
return 0;
}
static int SetExpandSandboxConfig(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox)
{
int ret = ProcessExpandAppSandboxConfig(context, sandbox, "HspList");
APPSPAWN_CHECK(ret == 0, return ret,
"Set HspList config fail result: %{public}d, app: %{public}s", ret, context->bundleName);
ret = ProcessExpandAppSandboxConfig(context, sandbox, "DataGroup");
APPSPAWN_CHECK(ret == 0, return ret,
"Set DataGroup config fail result: %{public}d, app: %{public}s", ret, context->bundleName);
bool mountDestBundlePath = false;
AppSpawnMsgDomainInfo *msgDomainInfo = (AppSpawnMsgDomainInfo *)GetSpawningMsgInfo(context, TLV_DOMAIN_INFO);
if (msgDomainInfo != NULL) {
mountDestBundlePath = (strcmp(msgDomainInfo->apl, APL_SYSTEM_BASIC) == 0) ||
(strcmp(msgDomainInfo->apl, APL_SYSTEM_CORE) == 0);
}
if (mountDestBundlePath || (CheckSpawningMsgFlagSet(context, APP_FLAGS_ACCESS_BUNDLE_DIR) != 0)) {
// need permission check for system app here
const char *destBundlesPath = GetSandboxRealVar(context,
BUFFER_FOR_TARGET, "/data/bundles/", context->sandboxPackagePath, NULL);
CreateSandboxDir(destBundlesPath, FILE_MODE);
MountArg mountArg = {PHYSICAL_APP_INSTALL_PATH, destBundlesPath, NULL, MS_REC | MS_BIND, NULL, MS_SLAVE};
ret = SandboxMountPath(&mountArg);
APPSPAWN_CHECK(ret == 0, return ret, "mount library failed %{public}d", ret);
}
return 0;
}
static int SetSandboxPackageNameConfig(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox)
{
SandboxPackageNameNode *sandboxNode =
(SandboxPackageNameNode *)GetSandboxSection(&sandbox->packageNameQueue, context->bundleName);
if (sandboxNode != NULL) {
int ret = MountSandboxConfig(context, sandbox, &sandboxNode->section, 0);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
}
return 0;
}
static int SetSandboxSpawnFlagsConfig(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox)
{
APPSPAWN_LOGV("Set spawning flags config");
ListNode *node = sandbox->spawnFlagsQueue.front.next;
while (node != &sandbox->spawnFlagsQueue.front) {
SandboxFlagsNode *sandboxNode = (SandboxFlagsNode *)ListEntry(node, SandboxMountNode, node);
if (!CheckSpawningMsgFlagSet(context, sandboxNode->flagIndex)) { // match flags point
node = node->next;
continue;
}
int ret = MountSandboxConfig(context, sandbox, &sandboxNode->section, 0);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
node = node->next;
}
return 0;
}
static int SetSandboxPermissionConfig(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox)
{
APPSPAWN_LOGV("Set permission config");
ListNode *node = sandbox->permissionQueue.front.next;
while (node != &sandbox->permissionQueue.front) {
SandboxPermissionNode *permissionNode = (SandboxPermissionNode *)ListEntry(node, SandboxMountNode, node);
if (!CheckSpawningPermissionFlagSet(context, permissionNode->permissionIndex)) {
node = node->next;
continue;
}
APPSPAWN_LOGV("SetSandboxPermissionConfig permission %{public}s", permissionNode->section.name);
int ret = MountSandboxConfig(context, sandbox, &permissionNode->section, 0);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
node = node->next;
}
return 0;
}
static int SetOverlayAppSandboxConfig(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox)
{
if (!CheckSpawningMsgFlagSet(context, APP_FLAGS_OVERLAY)) {
return 0;
}
int ret = ProcessExpandAppSandboxConfig(context, sandbox, "Overlay");
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
return 0;
}
static int SetBundleResourceSandboxConfig(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox)
{
if (!CheckSpawningMsgFlagSet(context, APP_FLAGS_BUNDLE_RESOURCES)) {
return 0;
}
const char *destPath = GetSandboxRealVar(context,
BUFFER_FOR_TARGET, "/data/storage/bundle_resources/", context->sandboxPackagePath, NULL);
CreateSandboxDir(destPath, FILE_MODE);
MountArg mountArg = {
"/data/service/el1/public/bms/bundle_resources/", destPath, NULL, MS_REC | MS_BIND, NULL, MS_SLAVE
};
int ret = SandboxMountPath(&mountArg);
return ret;
}
static int32_t ChangeCurrentDir(const SandboxContext *context)
{
int32_t ret = 0;
ret = chdir(context->sandboxPackagePath);
APPSPAWN_CHECK(ret == 0, return ret,
"chdir failed, app: %{public}s, path: %{public}s errno: %{public}d",
context->bundleName, context->sandboxPackagePath, errno);
if (context->sandboxShared) {
ret = chroot(context->sandboxPackagePath);
APPSPAWN_CHECK(ret == 0, return ret,
"chroot failed, path: %{public}s errno: %{public}d", context->sandboxPackagePath, errno);
return ret;
}
ret = syscall(SYS_pivot_root, context->sandboxPackagePath, context->sandboxPackagePath);
APPSPAWN_CHECK(ret == 0, return ret,
"pivot root failed, path: %{public}s errno: %{public}d", context->sandboxPackagePath, errno);
ret = umount2(".", MNT_DETACH);
APPSPAWN_CHECK(ret == 0, return ret,
"MNT_DETACH failed, path: %{public}s errno: %{public}d", context->sandboxPackagePath, errno);
APPSPAWN_LOGV("ChangeCurrentDir %{public}s ", context->sandboxPackagePath);
return ret;
}
static int SandboxRootFolderCreateNoShare(
const SandboxContext *context, const AppSpawnSandboxCfg *sandbox, bool remountProc)
{
APPSPAWN_LOGV("SandboxRootFolderCreateNoShare %{public}s ", context->sandboxPackagePath);
int ret = mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL);
APPSPAWN_CHECK(ret == 0, return ret,
"set propagation slave failed, app: %{public}s errno: %{public}d", context->sandboxPackagePath, errno);
MountArg arg = {context->sandboxPackagePath, context->sandboxPackagePath, NULL, BASIC_MOUNT_FLAGS, NULL, MS_SLAVE};
ret = SandboxMountPath(&arg);
APPSPAWN_CHECK(ret == 0, return ret,
"mount path failed, app: %{public}s errno: %{public}d", context->sandboxPackagePath, ret);
return ret;
}
static int SandboxRootFolderCreate(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox)
{
APPSPAWN_LOGV("topSandboxSwitch %{public}d sandboxSwitch: %{public}d sandboxShared: %{public}d \n",
sandbox->topSandboxSwitch, context->sandboxSwitch, context->sandboxShared);
int ret = 0;
if (sandbox->topSandboxSwitch == 0 || context->sandboxSwitch == 0) {
ret = mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL);
APPSPAWN_CHECK(ret == 0, return ret,
"set propagation slave failed, app: %{public}s errno: %{public}d", context->sandboxPackagePath, errno);
// bind mount "/" to /mnt/sandbox/<packageName> path
// rootfs: to do more resources bind mount here to get more strict resources constraints
ret = mount("/", context->sandboxPackagePath, NULL, BASIC_MOUNT_FLAGS, NULL);
APPSPAWN_CHECK(ret == 0, return ret,
"mount bind / failed, app: %{public}s errno: %{public}d", context->sandboxPackagePath, errno);
} else if (!context->sandboxShared) {
bool remountProc = !context->nwebspawn && ((sandbox->sandboxNsFlags & CLONE_NEWPID) == CLONE_NEWPID);
ret = SandboxRootFolderCreateNoShare(context, sandbox, remountProc);
}
return ret;
}
int DumpCurrentDir(SandboxContext *context, const char *dirPath)
{
DIR *pDir = opendir(dirPath);
APPSPAWN_CHECK(pDir != NULL, return -1, "Read dir :%{public}s failed.%{public}d", dirPath, errno);
struct dirent *dp;
while ((dp = readdir(pDir)) != NULL) {
if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
continue;
}
if (dp->d_type == DT_DIR) {
APPSPAWN_LOGV(" Current path %{public}s/%{public}s ", dirPath, dp->d_name);
APPSPAWN_CHECK_ONLY_EXPER(snprintf_s(context->buffer[1].buffer, context->buffer[1].bufferLen, context->buffer[1].bufferLen - 1,
"%s/%s", dirPath, dp->d_name) != -1, return -1);
char *path = strdup(context->buffer[1].buffer);
DumpCurrentDir(context, path);
free(path);
}
}
closedir(pDir);
return 0;
}
int MountSandboxConfigs(const AppSpawnSandboxCfg *sandbox, const AppSpawningCtx *property, int nwebspawn)
{
APPSPAWN_CHECK_ONLY_EXPER(property != NULL, return -1);
APPSPAWN_CHECK(sandbox != NULL, return -1, "Failed to get sandbox for %{public}s", GetProcessName(property));
SandboxContext *context = GetSandboxContext();
APPSPAWN_CHECK_ONLY_EXPER(context != NULL, return APPSPAWN_SYSTEM_ERROR);
int ret = InitSandboxContext(context, sandbox, property, nwebspawn);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
do {
ret = BuildRootPath((SandboxContext *)context, sandbox, NULL);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
ret = BuildPackagePath(context, sandbox);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
APPSPAWN_LOGV("Set sandbox config %{public}s", context->sandboxPackagePath);
ret = StagedMountPreUnShare(context, sandbox);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
CreateSandboxDir(context->sandboxPackagePath, FILE_MODE);
// add pid to a new mnt namespace
ret = unshare(CLONE_NEWNS);
APPSPAWN_CHECK(ret == 0, break,
"unshare failed, app: %{public}s errno: %{public}d", context->bundleName, errno);
ret = SandboxRootFolderCreate(context, sandbox);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
ret = StagedMountPostUnshare(context, sandbox);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
ret = SetOverlayAppSandboxConfig(context, sandbox);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
ret = SetBundleResourceSandboxConfig(context, sandbox);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
ret = ChangeCurrentDir(context);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
APPSPAWN_LOGV("Change root dir success %{public}s ", context->sandboxPackagePath);
} while (0);
if (getcwd(context->buffer[0].buffer, context->buffer[0].bufferLen) != NULL) {
APPSPAWN_LOGE("current_path %{public}s\n", context->buffer[0].buffer);
}
APPSPAWN_LOGV("current_path %{public}s \n", context->buffer[0].buffer);
DumpCurrentDir(context, "/data");
DeleteSandboxContext(context);
return ret;
}
int StagedMountSystemConst(const AppSpawnSandboxCfg *sandbox, const AppSpawningCtx *property, int nwebspawn)
{
APPSPAWN_CHECK(sandbox != NULL, return -1, "Failed to get sandbox for %{public}s", GetProcessName(property));
/**
* system-const
* root-dir "/mnt/sandbox/app-root/<currentUserId>"
* system-const mount path --
* src = mount-path.src-path
* dst = root-dir + mount-path.sandbox-path
*
* name-groupsmount path
* type system-const
* mount-paths-deps
* src = mount-path.src-path
* dst = root-dir + mount-paths-deps.sandbox-path + mount-path.sandbox-path
*
* src = mount-path.src-path
* dst = root-dir + mount-path.sandbox-path
*/
SandboxContext *context = GetSandboxContext();
APPSPAWN_CHECK_ONLY_EXPER(context != NULL, return APPSPAWN_SYSTEM_ERROR);
int ret = InitSandboxContext(context, sandbox, property, nwebspawn);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
BuildRootPath((SandboxContext *)context, sandbox, NULL);
SandboxSection *section = GetSandboxSection(&sandbox->requiredQueue, "system-const");
if (section != NULL) {
ret = MountSandboxConfig(context, sandbox, section, 0);
}
DeleteSandboxContext(context);
return ret;
}
int StagedMountPreUnShare(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox)
{
APPSPAWN_CHECK(sandbox != NULL && context != NULL, return -1, "Invalid sandbox or context");
APPSPAWN_LOGV("Set sandbox config before unshare group count %{public}d", sandbox->depNodeCount);
/**
* unshare前处理mount-paths-deps
* root-dir global.sandbox-root
* src-dir "/mnt/sandbox/app-common/<currentUserId>"
* mount-paths-depsmount-paths-deps
* src = mount-paths-deps.src-path
* dst = mount-paths-deps.sandbox-path
* no-existmount-paths src
mount-paths-deps.src-path shared方式挂载mount-paths-deps
* alwaysshared方式挂载mount-paths-deps
* always
*
*/
int ret = 0;
for (uint32_t i = 0; i < sandbox->depNodeCount; i++) {
SandboxNameGroupNode *groupNode = sandbox->depGroupNodes[i];
if (groupNode == NULL || groupNode->depNode == NULL) {
continue;
}
APPSPAWN_LOGV("Set sandbox deps config %{public}s ", groupNode->section.name);
// change source and target to real path
ret = UpdateMountPathDepsPath(context, groupNode);
APPSPAWN_CHECK(ret == 0, return ret,
"Failed to update deps path name group %{public}s", groupNode->section.name);
if (CheckAndCreateDepPath(context, groupNode)) {
continue;
}
ret = DoSandboxPathNodeMount(context, &groupNode->section, groupNode->depNode, 0);
if (ret != 0) {
APPSPAWN_LOGE("Mount deps root fail %{public}s", groupNode->section.name);
return ret;
}
if (groupNode->destType == SANDBOX_TAG_SYSTEM_CONST) {
ret = MountSandboxConfig(context, sandbox, &groupNode->section, MOUNT_PATH_OP_UNMOUNT);
APPSPAWN_CHECK(ret == 0, return ret,
"Set system-const config fail result: %{public}d, app: %{public}s", ret, context->bundleName);
}
}
return 0;
}
int StagedMountPostUnshare(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox)
{
APPSPAWN_CHECK(sandbox != NULL && context != NULL, return -1, "Invalid sandbox or context");
APPSPAWN_LOGV("Set sandbox config after unshare ");
/**
* app-variable
* root-dir global.sandbox-root
* app-variabl mount path
* src = mount-path.src-path
* dst = root-dir + mount-path.sandbox-path
* name-groupsmount path
* mount-paths-deps
* src = mount-path.src-path
* dst = root-dir + mount-paths-deps.sandbox-path + mount-path.sandbox-path
*
* src = mount-path.src-path
* dst = root-dir + mount-path.sandbox-path
*/
int ret = 0;
SandboxSection *section = GetSandboxSection(&sandbox->requiredQueue, "app-variable");
if (section != NULL) {
ret = MountSandboxConfig(context, sandbox, section, 0);
APPSPAWN_CHECK(ret == 0, return ret,
"Set app-variable config fail result: %{public}d, app: %{public}s", ret, context->bundleName);
}
if (!context->nwebspawn) {
ret = SetExpandSandboxConfig(context, sandbox);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
}
ret = SetSandboxSpawnFlagsConfig(context, sandbox);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
ret = SetSandboxPackageNameConfig(context, sandbox);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
ret = SetSandboxPermissionConfig(context, sandbox);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
return ret;
}
int UnmountSandboxConfigs(const AppSpawnSandboxCfg *sandbox)
{
APPSPAWN_CHECK(sandbox != NULL, return -1, "Invalid sandbox or context");
for (uint32_t i = 0; i < sandbox->depNodeCount; i++) {
SandboxNameGroupNode *groupNode = sandbox->depGroupNodes[i];
if (groupNode == NULL || groupNode->depNode == NULL) {
continue;
}
APPSPAWN_LOGV("Unmount sandbox config sandbox path %{public}s ", groupNode->depNode->target);
// unmount this deps
umount2(groupNode->depNode->target, MNT_DETACH);
}
return 0;
}

View File

@ -0,0 +1,325 @@
/*
* 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.
*/
#ifndef APPSPAWN_SANDBOX_H
#define APPSPAWN_SANDBOX_H
#include <limits.h>
#include ""
#include "appspawn.h"
#include "appspawn_hook.h"
#include "appspawn_utils.h"
#include "list.h"
#ifdef __cplusplus
extern "C" {
#endif
#define JSON_FLAGS_INTERNAL "__internal__"
#define SANDBOX_NWEBSPAWN_ROOT_PATH APPSPAWN_BASE_DIR "/mnt/sandbox/com.ohos.render/"
#define OHOS_RENDER "__internal__.com.ohos.render"
#define PHYSICAL_APP_INSTALL_PATH "/data/app/el1/bundle/public/"
#define APL_SYSTEM_CORE "system_core"
#define APL_SYSTEM_BASIC "system_basic"
#define MODULE_TEST_BUNDLE_NAME "moduleTestProcessName"
#define DEFAULT_NWEB_SANDBOX_SEC_PATH "/data/app/el1/bundle/public/com.ohos.nweb" // persist.nweb.sandbox.src_path
#define PARAMETER_PACKAGE_NAME "<PackageName>"
#define PARAMETER_USER_ID "<currentUserId>"
#define PARAMETER_PACKAGE_INDEX "<PackageName_index>"
#define FILE_MODE 0711
#define MAX_SANDBOX_BUFFER 256
#define FUSE_OPTIONS_MAX_LEN 256
#define DLP_FUSE_FD 1000
#define APP_FLAGS_SECTION 0x80000000
#define BASIC_MOUNT_FLAGS (MS_REC | MS_BIND)
#define INVALID_UID ((uint32_t)-1)
#ifdef APPSPAWN_64
#define APPSPAWN_LIB_NAME "lib64"
#else
#define APPSPAWN_LIB_NAME "lib"
#endif
#define MOUNT_MODE_NONE 0 // "none"
#define MOUNT_MODE_ALWAYS 1 // "always"
#define MOUNT_MODE_NOT_EXIST 2 // "not-exists"
#define MOUNT_PATH_OP_NO_SYMLINK 0x01
#define MOUNT_PATH_OP_UNMOUNT 0x02
#define FILE_CROSS_APP_MODE "ohos.permission.FILE_CROSS_APP"
typedef enum SandboxTag {
SANDBOX_TAG_MOUNT_PATH = 0,
SANDBOX_TAG_MOUNT_FILE,
SANDBOX_TAG_SYMLINK,
SANDBOX_TAG_PERMISSION,
SANDBOX_TAG_PACKAGE_NAME,
SANDBOX_TAG_SPAWN_FLAGS,
SANDBOX_TAG_NAME_GROUP,
SANDBOX_TAG_SYSTEM_CONST,
SANDBOX_TAG_APP_VARIABLE,
SANDBOX_TAG_APP_CONST,
SANDBOX_TAG_REQUIRED,
SANDBOX_TAG_INVALID
} SandboxNodeType;
typedef struct {
struct ListNode node;
uint32_t type;
} SandboxMountNode;
typedef struct TagSandboxQueue {
struct ListNode front;
uint32_t type;
} SandboxQueue;
typedef struct TagPathMountNode {
SandboxMountNode sandboxNode;
char *source; // source 目录一般是全局的fs 目录
char *target; // 沙盒化后的目录
mode_t destMode; // "dest-mode": "S_IRUSR | S_IWOTH | S_IRWXU " 默认值0
uint32_t mountSharedFlag : 1; // "mount-shared-flag" : "true", 默认值false
uint32_t checkErrorFlag : 1;
uint32_t category;
char *appAplName;
} PathMountNode;
typedef struct TagSymbolLinkNode {
SandboxMountNode sandboxNode;
char *target;
char *linkName;
mode_t destMode; // "dest-mode": "S_IRUSR | S_IWOTH | S_IRWXU "
uint32_t checkErrorFlag : 1;
} SymbolLinkNode;
typedef struct TagSandboxSection {
SandboxMountNode sandboxNode;
struct ListNode front; // mount-path
char *name;
uint32_t number : 16;
uint32_t gidCount : 16;
gid_t *gidTable; // "gids": [1006, 1008],
uint32_t sandboxSwitch : 1; // "sandbox-switch": "ON",
uint32_t sandboxShared : 1; // "sandbox-switch": "ON",
SandboxMountNode **nameGroups;
} SandboxSection;
typedef struct {
SandboxSection section;
} SandboxPackageNameNode;
typedef struct {
SandboxSection section;
uint32_t flagIndex;
} SandboxFlagsNode;
typedef struct TagSandboxGroupNode {
SandboxSection section;
uint32_t caps; // "caps": [ "shared" ],
uint32_t destType;
PathMountNode *depNode;
uint32_t mountMode;
} SandboxNameGroupNode;
typedef struct TagPermissionNode {
SandboxSection section;
int32_t permissionIndex;
} SandboxPermissionNode;
typedef struct TagAppSpawnSandboxCfg {
AppSpawnExtData extData;
SandboxQueue requiredQueue;
SandboxQueue permissionQueue;
SandboxQueue packageNameQueue; // SandboxSection
SandboxQueue spawnFlagsQueue;
SandboxQueue nameGroupsQueue;
uint32_t depNodeCount;
SandboxNameGroupNode **depGroupNodes;
int32_t maxPermissionIndex;
uint32_t sandboxNsFlags; // "sandbox-ns-flags": [ "pid", "net" ], // for appspawn and newspawn
// for comm section
uint32_t topSandboxSwitch : 1; // "top-sandbox-switch": "ON",
uint32_t appFullMountEnable : 1;
uint32_t pidNamespaceSupport : 1;
uint32_t systemUids[10];
char *rootPath;
} AppSpawnSandboxCfg;
enum {
BUFFER_FOR_SOURCE,
BUFFER_FOR_TARGET,
MAX_BUFFER
};
typedef struct TagSandboxBuffer {
uint32_t bufferLen;
uint32_t current;
char *buffer;
} SandboxBuffer;
typedef struct TagSandboxContext {
SandboxBuffer buffer[MAX_BUFFER];
const char *bundleName;
const AppSpawnMsgNode *message; // 修改成操作消息
uint32_t sandboxSwitch : 1;
uint32_t sandboxShared : 1;
uint32_t bundleHasWps : 1;
uint32_t dlpBundle : 1;
uint32_t dlpUiExtType : 1;
uint32_t appFullMountEnable : 1;
uint32_t nwebspawn : 1;
char *rootPath;
char *sandboxPackagePath;
} SandboxContext;
/**
* @brief AppSpawnSandboxCfg op
*
* @return AppSpawnSandboxCfg*
*/
AppSpawnSandboxCfg *CreateAppSpawnSandbox(void);
AppSpawnSandboxCfg *GetAppSpawnSandbox(const AppSpawnMgr *content);
void DeleteAppSpawnSandbox(AppSpawnSandboxCfg *sandbox);
int LoadAppSandboxConfig(AppSpawnSandboxCfg *sandbox, int nwebSpawn);
void DumpAppSpawnSandboxCfg(AppSpawnSandboxCfg *sandbox);
/**
* @brief SandboxSection op
*
*/
SandboxSection *CreateSandboxSection(const char *name, uint32_t dataLen, uint32_t type);
SandboxSection *GetSandboxSection(const SandboxQueue *queue, const char *name);
void AddSandboxSection(SandboxSection *node, SandboxQueue *queue);
void DeleteSandboxSection(SandboxSection *node);
__attribute__((always_inline)) inline uint32_t GetSectionType(const SandboxSection *section)
{
return section != NULL ? section->sandboxNode.type : SANDBOX_TAG_INVALID;
}
/**
* @brief SandboxMountNode op
*
*/
SandboxMountNode *CreateSandboxMountNode(uint32_t dataLen, uint32_t type);
SandboxMountNode *GetFirstSandboxMountNode(const SandboxSection *section);
void DeleteSandboxMountNode(SandboxMountNode *mountNode);
void AddSandboxMountNode(SandboxMountNode *node, SandboxSection *section);
/**
* @brief sandbox mount interface
*
*/
int MountSandboxConfigs(const AppSpawnSandboxCfg *sandbox, const AppSpawningCtx *property, int nwebspawn);
int StagedMountSystemConst(const AppSpawnSandboxCfg *sandbox, const AppSpawningCtx *property, int nwebspawn);
int StagedMountPreUnShare(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox);
int StagedMountPostUnshare(const SandboxContext *context, const AppSpawnSandboxCfg *sandbox);
int UnmountSandboxConfigs(const AppSpawnSandboxCfg *sandbox);
/**
* @brief Variable op
*
*/
typedef struct {
struct ListNode node;
ReplaceVarHandler replaceVar;
char name[0];
} AppSandboxVarNode;
typedef struct TagVarExtraData {
uint32_t sandboxTag;
union {
PathMountNode *depNode;
} data;
} VarExtraData;
void ClearVariable(void);
void AddDefaultVariable(void);
const char *GetSandboxRealVar(const SandboxContext *context,
uint32_t bufferType, const char *source, const char *prefix, const VarExtraData *extraData);
/**
* @brief expand config
*
*/
typedef struct {
struct ListNode node;
ProcessExpandSandboxCfg cfgHandle;
int prio;
char name[0];
} AppSandboxExpandAppCfgNode;
int ProcessExpandAppSandboxConfig(const SandboxContext *context,
const AppSpawnSandboxCfg *appSandBox, const char *name);
void AddDefaultExpandAppSandboxConfigHandle(void);
void ClearExpandAppSandboxConfigHandle(void);
__attribute__((always_inline)) inline void *GetSpawningMsgInfo(const SandboxContext *context, uint32_t type)
{
APPSPAWN_CHECK(context->message != NULL,
return NULL, "Invalid property for type %{public}u", type);
return GetAppSpawnMsgInfo(context->message, type);
}
/**
* @brief Sandbox Context op
*
* @return SandboxContext*
*/
SandboxContext *GetSandboxContext(void);
void DeleteSandboxContext(SandboxContext *context);
/**
* @brief defineMount Arg Template and operation
*
*/
enum {
MOUNT_TMP_DEFAULT,
MOUNT_TMP_RDONLY,
MOUNT_TMP_EPFS,
MOUNT_TMP_DAC_OVERRIDE,
MOUNT_TMP_FUSE,
MOUNT_TMP_DLP_FUSE,
MOUNT_TMP_SHRED,
MOUNT_TMP_MAX
};
typedef struct {
char *name;
uint32_t category;
const char *fsType;
unsigned long mountFlags;
const char *options;
mode_t mountSharedFlag;
} MountArgTemplate;
typedef struct {
const char *name;
unsigned long flags;
} SandboxFlagInfo;
uint32_t GetMountCategory(const char *name);
const MountArgTemplate *GetMountArgTemplate(uint32_t category);
const SandboxFlagInfo *GetSandboxFlagInfo(const char *key, const SandboxFlagInfo *flagsInfos, uint32_t count);
int GetPathMode(const char *name);
void DumpMountPathMountNode(const PathMountNode *pathNode);
#ifdef __cplusplus
}
#endif
#endif // APPSPAWN_SANDBOX_H

247
modules/sandbox/sandbox_cfgvar.c Executable file
View File

@ -0,0 +1,247 @@
/*
* 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 "appspawn_sandbox.h"
#include "appspawn_utils.h"
#include "modulemgr.h"
#include "parameter.h"
#include "securec.h"
struct ListNode g_sandboxVarList = {&g_sandboxVarList, &g_sandboxVarList};
static int VarPackageNameIndexReplace(const SandboxContext *context,
const char *buffer, uint32_t bufferLen, uint32_t *realLen, const VarExtraData *extraData)
{
AppSpawnMsgBundleInfo *bundleInfo = (
AppSpawnMsgBundleInfo *)GetSpawningMsgInfo(context, TLV_BUNDLE_INFO);
APPSPAWN_CHECK(bundleInfo != NULL, return APPSPAWN_TLV_NONE,
"No bundle info in msg %{public}s", context->bundleName);
int len = 0;
if (bundleInfo->bundleIndex > 0) {
len = sprintf_s((char *)buffer, bufferLen, "%s_%d", bundleInfo->bundleName, bundleInfo->bundleIndex);
} else {
len = sprintf_s((char *)buffer, bufferLen, "%s", bundleInfo->bundleName);
}
APPSPAWN_CHECK(len > 0 && ((uint32_t)len < bufferLen),
return -1, "Failed to format path app: %{public}s", context->bundleName);
*realLen = (uint32_t)len;
return 0;
}
static int VarPackageNameReplace(const SandboxContext *context,
const char *buffer, uint32_t bufferLen, uint32_t *realLen, const VarExtraData *extraData)
{
int len = sprintf_s((char *)buffer, bufferLen, "%s", context->bundleName);
APPSPAWN_CHECK(len > 0 && ((uint32_t)len < bufferLen),
return -1, "Failed to format path app: %{public}s", context->bundleName);
*realLen = (uint32_t)len;
return 0;
}
static int VarCurrentUseIdReplace(const SandboxContext *context,
const char *buffer, uint32_t bufferLen, uint32_t *realLen, const VarExtraData *extraData)
{
AppSpawnMsgDacInfo *info = (AppSpawnMsgDacInfo *)GetSpawningMsgInfo(context, TLV_DAC_INFO);
APPSPAWN_CHECK(info != NULL, return APPSPAWN_TLV_NONE,
"No tlv %{public}d in msg %{public}s", TLV_DAC_INFO, context->bundleName);
int len = 0;
if (extraData == NULL || extraData->sandboxTag != SANDBOX_TAG_PERMISSION) {
len = sprintf_s((char *)buffer, bufferLen, "%u", info->uid / UID_BASE);
} else if (context->appFullMountEnable && strlen(info->userName) > 0) {
len = sprintf_s((char *)buffer, bufferLen, "%s", info->userName);
} else {
len = sprintf_s((char *)buffer, bufferLen, "%s", "currentUser");
}
APPSPAWN_CHECK(len > 0 && ((uint32_t)len < bufferLen),
return -1, "Failed to format path app: %{public}s", context->bundleName);
*realLen = (uint32_t)len;
return 0;
}
static int VariableNodeCompareName(ListNode *node, void *data)
{
AppSandboxVarNode *varNode = (AppSandboxVarNode *)ListEntry(node, AppSandboxVarNode, node);
return strcmp((char *)data, varNode->name);
}
static AppSandboxVarNode *GetAppSandboxVarNode(const char *name)
{
ListNode *node = OH_ListFind(&g_sandboxVarList, (void *)name, VariableNodeCompareName);
if (node == NULL) {
return NULL;
}
return (AppSandboxVarNode *)ListEntry(node, AppSandboxVarNode, node);
}
static int ReplaceVariableByParameter(const char *varData, SandboxBuffer *sandboxBuffer)
{
// "<param:persist.nweb.sandbox.src_path>"
int len = GetParameter(varData + sizeof("<param:") - 1,
DEFAULT_NWEB_SANDBOX_SEC_PATH, sandboxBuffer->buffer + sandboxBuffer->current,
sandboxBuffer->bufferLen - sandboxBuffer->current - 1);
APPSPAWN_CHECK(len > 0, return -1, "Failed to get param for var %{public}s", varData);
sandboxBuffer->current += len;
return 0;
}
static int ReplaceVariableForDeps(const SandboxContext *context,
SandboxBuffer *sandboxBuffer, const VarExtraData *extraData)
{
APPSPAWN_CHECK(extraData != NULL, return -1, "Invalid extra data ");
uint32_t len = strlen(extraData->data.depNode->target);
int ret = memcpy_s(sandboxBuffer->buffer + sandboxBuffer->current,
sandboxBuffer->bufferLen - sandboxBuffer->current, extraData->data.depNode->target, len);
APPSPAWN_CHECK(ret == 0, return -1, "Failed to copy real data");
sandboxBuffer->current += len;
return 0;
}
static int GetVariableName(char *varData, uint32_t len, const char *varStart, uint32_t *varLen)
{
uint32_t i = 0;
uint32_t sourceLen = strlen(varStart);
for (; i < sourceLen; i++) {
if (i > len) {
return -1;
}
varData[i] = *(varStart + i);
if (varData[i] == '>') {
break;
}
}
varData[i + 1] = '\0';
*varLen = i + 1;
return 0;
}
static int ReplaceVariable(const SandboxContext *context,
const char *varStart, SandboxBuffer *sandboxBuffer, uint32_t *varLen, const VarExtraData *extraData)
{
char varData[128] = {0}; // 128 max len for var
int ret = GetVariableName(varData, sizeof(varData), varStart, varLen);
APPSPAWN_CHECK(ret == 0, return -1, "Failed to get variable name");
uint32_t valueLen = 0;
AppSandboxVarNode *node = GetAppSandboxVarNode(varData);
if (node != NULL) {
ret = node->replaceVar(context, sandboxBuffer->buffer + sandboxBuffer->current,
sandboxBuffer->bufferLen - sandboxBuffer->current - 1, &valueLen, extraData);
APPSPAWN_CHECK(ret == 0 && valueLen < (sandboxBuffer->bufferLen - sandboxBuffer->current),
return -1, "Failed to fill real data");
sandboxBuffer->current += valueLen;
return 0;
}
// "<param:persist.nweb.sandbox.src_path>"
if (strncmp(varData, "<param:", sizeof("<param:") - 1) == 0) { // retry param:
varData[*varLen - 1] = '\0'; // erase last >
return ReplaceVariableByParameter(varData, sandboxBuffer);
}
if (strncmp(varData, "<lib>", sizeof("<lib>") - 1) == 0) { // retry lib
ret = memcpy_s(sandboxBuffer->buffer + sandboxBuffer->current,
sandboxBuffer->bufferLen - sandboxBuffer->current, APPSPAWN_LIB_NAME, strlen(APPSPAWN_LIB_NAME));
APPSPAWN_CHECK(ret == 0, return -1, "Failed to copy real data");
sandboxBuffer->current += strlen(APPSPAWN_LIB_NAME);
return 0;
}
// <deps-sandbox-path>
if (strncmp(varData, "<deps-sandbox-path>", sizeof("<deps-sandbox-path>") - 1) == 0) {
return ReplaceVariableForDeps(context, sandboxBuffer, extraData);
}
// no match revered origin data
APPSPAWN_LOGE("ReplaceVariable var '%{public}s' no match variable", varData);
ret = memcpy_s(sandboxBuffer->buffer + sandboxBuffer->current,
sandboxBuffer->bufferLen - sandboxBuffer->current, varData, *varLen);
APPSPAWN_CHECK(ret == 0, return -1, "Failed to copy real data");
sandboxBuffer->current += *varLen;
return 0;
}
static int HandleVariableReplace(const SandboxContext *context, SandboxBuffer *sandboxBuffer,
const char *source, const VarExtraData *extraData)
{
size_t sourceLen = strlen(source);
for (size_t i = 0; i < sourceLen; i++) {
if ((sandboxBuffer->current + 1) >= sandboxBuffer->bufferLen) {
return -1;
}
if (*(source + i) != '<') { // copy source
*(sandboxBuffer->buffer + sandboxBuffer->current) = *(source + i);
sandboxBuffer->current++;
continue;
}
uint32_t varLen = 0;
int ret = ReplaceVariable(context, source + i, sandboxBuffer, &varLen, extraData);
APPSPAWN_CHECK(ret == 0, return ret, "Failed to fill real data");
i += (varLen - 1);
}
return 0;
}
const char *GetSandboxRealVar(const SandboxContext *context,
uint32_t bufferType, const char *source, const char *prefix, const VarExtraData *extraData)
{
APPSPAWN_CHECK_ONLY_EXPER(context != NULL, return NULL);
APPSPAWN_CHECK(bufferType < ARRAY_LENGTH(context->buffer), return NULL, "Invalid index for buffer");
SandboxBuffer *sandboxBuffer = &((SandboxContext *)context)->buffer[bufferType];
const char *tmp = source;
int ret = 0;
if (prefix != NULL) { // copy prefix data
ret = HandleVariableReplace(context, sandboxBuffer, prefix, extraData);
APPSPAWN_CHECK(ret == 0, return NULL, "Failed to replace source %{public}s ", prefix);
if (tmp != NULL && sandboxBuffer->buffer[sandboxBuffer->current - 1] == '/' && *tmp == '/') {
tmp = source + 1;
}
}
if (tmp != NULL) { // copy source data
ret = HandleVariableReplace(context, sandboxBuffer, tmp, extraData);
APPSPAWN_CHECK(ret == 0, return NULL, "Failed to replace source %{public}s ", source);
}
sandboxBuffer->buffer[sandboxBuffer->current] = '\0';
// restore buffer
sandboxBuffer->current = 0;
return sandboxBuffer->buffer;
}
int AddVariableReplaceHandler(const char *name, ReplaceVarHandler handler)
{
APPSPAWN_CHECK(name != NULL && handler != NULL, return APPSPAWN_ARG_INVALID, "Invalid arg ");
if (GetAppSandboxVarNode(name) != NULL) {
return APPSPAWN_NODE_EXIST;
}
size_t len = APPSPAWN_ALIGN(strlen(name) + 1);
AppSandboxVarNode *node = (AppSandboxVarNode *)malloc(sizeof(AppSandboxVarNode) + len);
APPSPAWN_CHECK(node != NULL, return APPSPAWN_SYSTEM_ERROR, "Failed to create sandbox");
OH_ListInit(&node->node);
node->replaceVar = handler;
int ret = strcpy_s(node->name, len, name);
APPSPAWN_CHECK(ret == 0, free(node);
return -1, "Failed to copy name %{public}s", name);
OH_ListAddTail(&g_sandboxVarList, &node->node);
return 0;
}
void AddDefaultVariable(void)
{
AddVariableReplaceHandler(PARAMETER_PACKAGE_NAME, VarPackageNameReplace);
AddVariableReplaceHandler(PARAMETER_USER_ID, VarCurrentUseIdReplace);
AddVariableReplaceHandler(PARAMETER_PACKAGE_INDEX, VarPackageNameIndexReplace);
}
void ClearVariable(void)
{
OH_ListRemoveAll(&g_sandboxVarList, NULL);
}

298
modules/sandbox/sandbox_expand.c Executable file
View File

@ -0,0 +1,298 @@
/*
* 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 <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include "appspawn_msg.h"
#include "appspawn_sandbox.h"
#include "appspawn_utils.h"
#include "json_utils.h"
#include "securec.h"
#define SANDBOX_GROUP_PATH "/data/storage/el2/group/"
#define SANDBOX_INSTALL_PATH "/data/storage/el2/group/"
#define SANDBOX_OVERLAY_PATH "/data/storage/overlay/"
static inline bool CheckPath(const char *name)
{
return name != NULL && strcmp(name, ".") != 0 && strcmp(name, "..") != 0 && strstr(name, "/") == NULL;
}
static int MountAllHsp(const SandboxContext *context, const cJSON *hsps)
{
APPSPAWN_CHECK(context != NULL && hsps != NULL, return -1, "Invalid context or hsps");
int ret = 0;
cJSON *bundles = cJSON_GetObjectItemCaseSensitive(hsps, "bundles");
cJSON *modules = cJSON_GetObjectItemCaseSensitive(hsps, "modules");
cJSON *versions = cJSON_GetObjectItemCaseSensitive(hsps, "versions");
APPSPAWN_CHECK(bundles != NULL && cJSON_IsArray(bundles), return -1, "MountAllHsp: invalid bundles");
APPSPAWN_CHECK(modules != NULL && cJSON_IsArray(modules), return -1, "MountAllHsp: invalid modules");
APPSPAWN_CHECK(versions != NULL && cJSON_IsArray(versions), return -1, "MountAllHsp: invalid versions");
int count = cJSON_GetArraySize(bundles);
APPSPAWN_CHECK(count == cJSON_GetArraySize(modules), return -1, "MountAllHsp: sizes are not same");
APPSPAWN_CHECK(count == cJSON_GetArraySize(versions), return -1, "MountAllHsp: sizes are not same");
APPSPAWN_LOGI("MountAllHsp app: %{public}s, count: %{public}d", context->bundleName, count);
for (int i = 0; i < count; i++) {
char *libBundleName = cJSON_GetStringValue(cJSON_GetArrayItem(bundles, i));
char *libModuleName = cJSON_GetStringValue(cJSON_GetArrayItem(modules, i));
char *libVersion = cJSON_GetStringValue(cJSON_GetArrayItem(versions, i));
APPSPAWN_CHECK(CheckPath(libBundleName) && CheckPath(libModuleName) && CheckPath(libVersion),
return -1, "MountAllHsp: path error");
// src path
int len = sprintf_s(context->buffer[0].buffer, context->buffer[0].bufferLen, "%s%s/%s/%s",
PHYSICAL_APP_INSTALL_PATH, libBundleName, libVersion, libModuleName);
APPSPAWN_CHECK(len > 0, return -1, "Failed to format install path");
// sandbox path
len = sprintf_s(context->buffer[1].buffer, context->buffer[1].bufferLen, "%s%s%s/%s",
context->sandboxPackagePath, SANDBOX_INSTALL_PATH, libBundleName, libModuleName);
APPSPAWN_CHECK(len > 0, return -1, "Failed to format install path");
CreateSandboxDir(context->buffer[1].buffer, FILE_MODE);
MountArg mountArg = {
context->buffer[0].buffer, context->buffer[1].buffer, NULL, MS_REC | MS_BIND, NULL, MS_SLAVE
};
ret = SandboxMountPath(&mountArg);
APPSPAWN_CHECK(ret == 0, return ret, "mount library failed %{public}d", ret);
}
return ret;
}
static inline char *GetLastPath(const char *libPhysicalPath)
{
char *tmp = GetLastStr(libPhysicalPath, "/");
return tmp + 1;
}
static int MountAllGroup(const SandboxContext *context, const cJSON *groups)
{
APPSPAWN_CHECK(context != NULL && groups != NULL, return -1, "Invalid context or group");
int ret = 0;
cJSON *dataGroupIds = cJSON_GetObjectItemCaseSensitive(groups, "dataGroupId");
cJSON *gids = cJSON_GetObjectItemCaseSensitive(groups, "gid");
cJSON *dirs = cJSON_GetObjectItemCaseSensitive(groups, "dir");
APPSPAWN_CHECK(dataGroupIds != NULL && cJSON_IsArray(dataGroupIds),
return -1, "MountAllGroup: invalid dataGroupIds");
APPSPAWN_CHECK(gids != NULL && cJSON_IsArray(gids), return -1, "MountAllGroup: invalid gids");
APPSPAWN_CHECK(dirs != NULL && cJSON_IsArray(dirs), return -1, "MountAllGroup: invalid dirs");
int count = cJSON_GetArraySize(dataGroupIds);
APPSPAWN_CHECK(count == cJSON_GetArraySize(gids), return -1, "MountAllGroup: sizes are not same");
APPSPAWN_CHECK(count == cJSON_GetArraySize(dirs), return -1, "MountAllGroup: sizes are not same");
APPSPAWN_LOGI("MountAllGroup: app: %{public}s, count: %{public}d", context->bundleName, count);
for (int i = 0; i < count; i++) {
cJSON *dirJson = cJSON_GetArrayItem(dirs, i);
APPSPAWN_CHECK(dirJson != NULL && cJSON_IsString(dirJson), return -1, "MountAllGroup: invalid dirJson");
const char *libPhysicalPath = cJSON_GetStringValue(dirJson);
APPSPAWN_CHECK(!CheckPath(libPhysicalPath), return -1, "MountAllGroup: path error");
char *dataGroupUuid = GetLastPath(libPhysicalPath);
int len = sprintf_s(context->buffer[0].buffer, context->buffer[0].bufferLen, "%s%s%s",
context->sandboxPackagePath, SANDBOX_GROUP_PATH, dataGroupUuid);
APPSPAWN_CHECK(len > 0, return -1, "Failed to format install path");
APPSPAWN_LOGV("MountAllGroup src: '%{public}s' =>'%{public}s'", libPhysicalPath, context->buffer[0].buffer);
CreateSandboxDir(context->buffer[0].buffer, FILE_MODE);
MountArg mountArg = {libPhysicalPath, context->buffer[0].buffer, NULL, MS_REC | MS_BIND, NULL, MS_SLAVE};
ret = SandboxMountPath(&mountArg);
APPSPAWN_CHECK(ret == 0, return ret, "mount library failed %{public}d", ret);
}
return ret;
}
typedef struct {
const SandboxContext *sandboxContext;
uint32_t srcSetLen;
char *mountedSrcSet;
} OverlayContext;
static int SetOverlayAppPath(const char *hapPath, void *context)
{
APPSPAWN_LOGV("SetOverlayAppPath '%{public}s'", hapPath);
OverlayContext *overlayContext = (OverlayContext *)context;
const SandboxContext *sandboxContext = overlayContext->sandboxContext;
// src path
char *tmp = GetLastStr(hapPath, "/");
if (tmp == NULL) {
return 0;
}
int ret = strncpy_s(sandboxContext->buffer[0].buffer,
sandboxContext->buffer[0].bufferLen, hapPath, tmp - (char *)hapPath);
APPSPAWN_CHECK(ret == 0, return ret, "mount library failed %{public}d", ret);
if (strstr(overlayContext->mountedSrcSet, sandboxContext->buffer[0].buffer) != NULL) {
APPSPAWN_LOGV("%{public}s have mounted before, no need to mount twice.", sandboxContext->buffer[0].buffer);
return 0;
}
ret = strcat_s(overlayContext->mountedSrcSet, overlayContext->srcSetLen, "|");
APPSPAWN_CHECK(ret == 0, return ret, "Fail to add src path to set %{public}s", "|");
ret = strcat_s(overlayContext->mountedSrcSet, overlayContext->srcSetLen, sandboxContext->buffer[0].buffer);
APPSPAWN_CHECK(ret == 0, return ret, "Fail to add src path to set %{public}s", sandboxContext->buffer[0].buffer);
// sandbox path
tmp = GetLastStr(sandboxContext->buffer[0].buffer, "/");
if (tmp == NULL) {
return 0;
}
int len = sprintf_s(sandboxContext->buffer[1].buffer, sandboxContext->buffer[1].bufferLen, "%s%s",
sandboxContext->sandboxPackagePath, SANDBOX_OVERLAY_PATH);
APPSPAWN_CHECK(len > 0, return -1, "Failed to format install path");
ret = strcat_s(sandboxContext->buffer[1].buffer, sandboxContext->buffer[1].bufferLen - len, tmp + 1);
APPSPAWN_CHECK(ret == 0, return ret, "mount library failed %{public}d", ret);
APPSPAWN_LOGV("SetOverlayAppPath path: '%{public}s' => '%{public}s'",
sandboxContext->buffer[0].buffer, sandboxContext->buffer[1].buffer);
MountArg mountArg = {
sandboxContext->buffer[0].buffer, sandboxContext->buffer[1].buffer, NULL, MS_REC | MS_BIND, NULL, MS_SHARED
};
int retMount = SandboxMountPath(&mountArg);
if (retMount != 0) {
APPSPAWN_LOGE("Fail to mount overlay path, src is %{public}s.", hapPath);
ret = retMount;
}
return ret;
}
static int SetOverlayAppSandboxConfig(const SandboxContext *context, const char *overlayInfo)
{
APPSPAWN_CHECK(context != NULL && overlayInfo != NULL, return -1, "Invalid context or overlayInfo");
OverlayContext overlayContext;
overlayContext.sandboxContext = context;
overlayContext.srcSetLen = strlen(overlayInfo);
overlayContext.mountedSrcSet = (char *)calloc(1, overlayContext.srcSetLen + 1);
APPSPAWN_CHECK(overlayContext.mountedSrcSet != NULL, return -1, "Failed to create mountedSrcSet");
*(overlayContext.mountedSrcSet + overlayContext.srcSetLen) = '\0';
int ret = StringSplit(overlayInfo, "|", (void *)&overlayContext, SetOverlayAppPath);
APPSPAWN_LOGV("overlayContext->mountedSrcSet: '%{public}s'", overlayContext.mountedSrcSet);
free(overlayContext.mountedSrcSet);
return ret;
}
static inline cJSON *GetJsonObjFromProperty(const SandboxContext *context, const char *name)
{
uint32_t size = 0;
char *extInfo = (char *)(GetAppSpawnMsgExtInfo(context->message, name, &size));
if (size == 0 || extInfo == NULL) {
return NULL;
}
APPSPAWN_LOGV("Get json name %{public}s value %{public}s", name, extInfo);
cJSON *root = cJSON_Parse(extInfo);
APPSPAWN_CHECK(root != NULL, return NULL, "Invalid ext info %{public}s for %{public}s", extInfo, name);
return root;
}
static int ProcessHSPListConfig(const SandboxContext *context, const AppSpawnSandboxCfg *appSandBox, const char *name)
{
cJSON *root = GetJsonObjFromProperty(context, name);
APPSPAWN_CHECK_ONLY_EXPER(root != NULL, return 0);
int ret = MountAllHsp(context, root);
cJSON_Delete(root);
return ret;
}
static int ProcessDataGroupConfig(const SandboxContext *context, const AppSpawnSandboxCfg *appSandBox, const char *name)
{
cJSON *root = GetJsonObjFromProperty(context, name);
APPSPAWN_CHECK_ONLY_EXPER(root != NULL, return 0);
int ret = MountAllGroup(context, root);
cJSON_Delete(root);
return ret;
}
static int ProcessOverlayAppConfig(const SandboxContext *context,
const AppSpawnSandboxCfg *appSandBox, const char *name)
{
uint32_t size = 0;
char *extInfo = (char *)GetAppSpawnMsgExtInfo(context->message, name, &size);
if (size == 0 || extInfo == NULL) {
return 0;
}
APPSPAWN_LOGV("ProcessOverlayAppConfig name %{public}s value %{public}s", name, extInfo);
return SetOverlayAppSandboxConfig(context, extInfo);
}
struct ListNode g_sandboxExpandCfgList = {&g_sandboxExpandCfgList, &g_sandboxExpandCfgList};
static int AppSandboxExpandAppCfgCompareName(ListNode *node, void *data)
{
AppSandboxExpandAppCfgNode *varNode = ListEntry(node, AppSandboxExpandAppCfgNode, node);
return strncmp((char *)data, varNode->name, strlen(varNode->name));
}
static int AppSandboxExpandAppCfgComparePrio(ListNode *node1, ListNode *node2)
{
AppSandboxExpandAppCfgNode *varNode1 = ListEntry(node1, AppSandboxExpandAppCfgNode, node);
AppSandboxExpandAppCfgNode *varNode2 = ListEntry(node2, AppSandboxExpandAppCfgNode, node);
return varNode1->prio - varNode2->prio;
}
static const AppSandboxExpandAppCfgNode *GetAppSandboxExpandAppCfg(const char *name)
{
ListNode *node = OH_ListFind(&g_sandboxExpandCfgList, (void *)name, AppSandboxExpandAppCfgCompareName);
if (node == NULL) {
return NULL;
}
return ListEntry(node, AppSandboxExpandAppCfgNode, node);
}
int RegisterExpandSandboxCfgHandler(const char *name, int prio, ProcessExpandSandboxCfg handleExpandCfg)
{
APPSPAWN_CHECK_ONLY_EXPER(name != NULL && handleExpandCfg != NULL, return APPSPAWN_ARG_INVALID);
if (GetAppSandboxExpandAppCfg(name) != NULL) {
return APPSPAWN_NODE_EXIST;
}
size_t len = APPSPAWN_ALIGN(strlen(name) + 1);
AppSandboxExpandAppCfgNode *node = (AppSandboxExpandAppCfgNode *)(malloc(sizeof(AppSandboxExpandAppCfgNode) + len));
APPSPAWN_CHECK(node != NULL, return APPSPAWN_SYSTEM_ERROR, "Failed to create sandbox");
// ext data init
OH_ListInit(&node->node);
node->cfgHandle = handleExpandCfg;
node->prio = prio;
int ret = strcpy_s(node->name, len, name);
APPSPAWN_CHECK(ret == 0, free(node);
return -1, "Failed to copy name %{public}s", name);
OH_ListAddWithOrder(&g_sandboxExpandCfgList, &node->node, AppSandboxExpandAppCfgComparePrio);
return 0;
}
int ProcessExpandAppSandboxConfig(const SandboxContext *context, const AppSpawnSandboxCfg *appSandBox, const char *name)
{
APPSPAWN_CHECK_ONLY_EXPER(context != NULL && appSandBox != NULL, return APPSPAWN_ARG_INVALID);
APPSPAWN_CHECK_ONLY_EXPER(name != NULL, return APPSPAWN_ARG_INVALID);
APPSPAWN_LOGV("ProcessExpandAppSandboxConfig %{public}s.", name);
const AppSandboxExpandAppCfgNode *node = GetAppSandboxExpandAppCfg(name);
if (node != NULL && node->cfgHandle != NULL) {
return node->cfgHandle(context, appSandBox, name);
}
return 0;
}
void AddDefaultExpandAppSandboxConfigHandle(void)
{
RegisterExpandSandboxCfgHandler("HspList", 0, ProcessHSPListConfig);
RegisterExpandSandboxCfgHandler("DataGroup", 1, ProcessDataGroupConfig);
RegisterExpandSandboxCfgHandler("Overlay", 2, ProcessOverlayAppConfig); // 2 priority
}
void ClearExpandAppSandboxConfigHandle(void)
{
OH_ListRemoveAll(&g_sandboxExpandCfgList, NULL);
}

578
modules/sandbox/sandbox_load.c Executable file
View File

@ -0,0 +1,578 @@
/*
* 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 <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/types.h>
#include "appspawn_msg.h"
#include "appspawn_permission.h"
#include "appspawn_sandbox.h"
#include "appspawn_utils.h"
#include "cJSON.h"
#include "init_utils.h"
#include "json_utils.h"
#include "parameter.h"
#include "securec.h"
static const SandboxFlagInfo NAMESPACE_FLAGS_MAP[] = {
{"pid", CLONE_NEWPID}, {"net", CLONE_NEWNET}
};
static const SandboxFlagInfo FLAGE_POINT_MAP[] = {
{"0", 0},
{"START_FLAGS_BACKUP", (unsigned long)APP_FLAGS_BACKUP_EXTENSION},
{"DLP_MANAGER", (unsigned long)APP_FLAGS_DLP_MANAGER}
};
static const SandboxFlagInfo MOUNT_MODE_MAP[] = {
{"not-exists", (unsigned long)MOUNT_MODE_NOT_EXIST},
{"always", (unsigned long)MOUNT_MODE_ALWAYS}
};
static const SandboxFlagInfo NAME_GROUP_TYPE_MAP[] = {
{"system-const", (unsigned long)SANDBOX_TAG_SYSTEM_CONST},
{"app-variable", (unsigned long)SANDBOX_TAG_APP_VARIABLE}
};
static inline PathMountNode *CreatePathMountNode(uint32_t type)
{
return (PathMountNode *)CreateSandboxMountNode(sizeof(PathMountNode), type);
}
static inline SymbolLinkNode *CreateSymbolLinkNode(void)
{
return (SymbolLinkNode *)CreateSandboxMountNode(sizeof(SymbolLinkNode), SANDBOX_TAG_SYMLINK);
}
static inline SandboxPackageNameNode *CreateSandboxPackageNameNode(const char *name)
{
return (SandboxPackageNameNode *)CreateSandboxSection(name,
sizeof(SandboxPackageNameNode), SANDBOX_TAG_PACKAGE_NAME);
}
static inline SandboxFlagsNode *CreateSandboxFlagsNode(const char *name)
{
return (SandboxFlagsNode *)CreateSandboxSection(name, sizeof(SandboxFlagsNode), SANDBOX_TAG_SPAWN_FLAGS);
}
static inline SandboxNameGroupNode *CreateSandboxNameGroupNode(const char *name)
{
return (SandboxNameGroupNode *)CreateSandboxSection(name,
sizeof(SandboxNameGroupNode), SANDBOX_TAG_NAME_GROUP);
}
static inline SandboxPermissionNode *CreateSandboxPermissionNode(const char *name)
{
size_t len = sizeof(SandboxPermissionNode);
SandboxPermissionNode *node = (SandboxPermissionNode *)
CreateSandboxSection(name, len, SANDBOX_TAG_PERMISSION);
APPSPAWN_CHECK(node != NULL, return NULL, "Failed to create permission node");
node->permissionIndex = 0;
return node;
}
static inline bool GetBoolParameter(const char *param, bool value)
{
char tmp[32] = {0}; // 32 max
int ret = GetParameter(param, "", tmp, sizeof(tmp));
APPSPAWN_LOGV("GetBoolParameter key %{public}s ret %{public}d result: %{public}s", param, ret, tmp);
return (ret > 0 && (strcmp(tmp, "true") == 0 || strcmp(tmp, "on") == 0 || strcmp(tmp, "1") == 0));
}
static inline bool AppSandboxPidNsIsSupport(void)
{
return GetBoolParameter("const.sandbox.pidns.support", true);
}
static inline bool CheckAppFullMountEnable(void)
{
return GetBoolParameter("const.filemanager.full_mount.enable", false);
}
static unsigned long GetMountModeFromConfig(const cJSON *config, const char *key, unsigned long def)
{
char *value = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(config, key));
if (value == NULL) {
return def;
}
const SandboxFlagInfo *info = GetSandboxFlagInfo(value, MOUNT_MODE_MAP, ARRAY_LENGTH(MOUNT_MODE_MAP));
if (info != NULL) {
return info->flags;
}
return def;
}
static uint32_t GetNameGroupTypeFromConfig(const cJSON *config, const char *key, unsigned long def)
{
char *value = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(config, key));
if (value == NULL) {
return def;
}
const SandboxFlagInfo *info = GetSandboxFlagInfo(value, NAME_GROUP_TYPE_MAP, ARRAY_LENGTH(NAME_GROUP_TYPE_MAP));
if (info != NULL) {
return (uint32_t)info->flags;
}
return def;
}
static uint32_t GetSandboxNsFlags(const cJSON *appConfig)
{
uint32_t nsFlags = 0;
cJSON *obj = cJSON_GetObjectItemCaseSensitive(appConfig, "sandbox-ns-flags");
if (obj == NULL || !cJSON_IsArray(obj)) {
return nsFlags;
}
int count = cJSON_GetArraySize(obj);
for (int i = 0; i < count; i++) {
char *value = cJSON_GetStringValue(cJSON_GetArrayItem(obj, i));
const SandboxFlagInfo *info = GetSandboxFlagInfo(value, NAMESPACE_FLAGS_MAP, ARRAY_LENGTH(NAMESPACE_FLAGS_MAP));
if (info != NULL) {
nsFlags |= info->flags;
}
}
return nsFlags;
}
static int SetMode(const char *str, void *context)
{
mode_t *mode = (mode_t *)context;
*mode |= GetPathMode(str);
return 0;
}
static mode_t GetChmodFromJson(const cJSON *config)
{
mode_t mode = 0;
char *modeStrs = GetStringFromJsonObj(config, "dest-mode");
if (modeStrs == NULL) {
return mode;
}
(void)StringSplit(modeStrs, "|", (void *)&mode, SetMode);
return mode;
}
static uint32_t GetFlagIndexFromJson(const cJSON *config)
{
char *flagStr = GetStringFromJsonObj(config, "name");
if (flagStr == NULL) {
return 0;
}
const SandboxFlagInfo *info = GetSandboxFlagInfo(flagStr, FLAGE_POINT_MAP, ARRAY_LENGTH(FLAGE_POINT_MAP));
if (info != NULL) {
return info->flags;
}
return 0;
}
static PathMountNode *DecodeMountPathConfig(const cJSON *config, uint32_t type)
{
char *srcPath = GetStringFromJsonObj(config, "src-path");
char *dstPath = GetStringFromJsonObj(config, "sandbox-path");
if (srcPath == NULL || dstPath == NULL) {
return NULL;
}
PathMountNode *sandboxNode = CreatePathMountNode(type);
APPSPAWN_CHECK_ONLY_EXPER(sandboxNode != NULL, return NULL);
sandboxNode->source = strdup(srcPath);
sandboxNode->target = strdup(dstPath);
sandboxNode->destMode = GetChmodFromJson(config);
sandboxNode->mountSharedFlag = GetBoolValueFromJsonObj(config, "mount-shared-flag", false);
sandboxNode->checkErrorFlag = GetBoolValueFromJsonObj(config, "check-action-status", false);
sandboxNode->category = GetMountCategory(GetStringFromJsonObj(config, "category"));
const char *value = GetStringFromJsonObj(config, "app-apl-name");
if (value != NULL) {
sandboxNode->appAplName = strdup(value);
}
if (sandboxNode->source == NULL || sandboxNode->target == NULL) {
APPSPAWN_LOGE("Failed to get sourc or target path");
DeleteSandboxMountNode((SandboxMountNode *)sandboxNode);
return NULL;
}
return sandboxNode;
}
static int ParseMountPathsConfig(AppSpawnSandboxCfg *sandbox,
const cJSON *mountConfigs, SandboxSection *section, uint32_t type)
{
APPSPAWN_CHECK_ONLY_EXPER(mountConfigs != NULL && cJSON_IsArray(mountConfigs), return -1);
uint32_t mountPointSize = cJSON_GetArraySize(mountConfigs);
for (uint32_t i = 0; i < mountPointSize; i++) {
cJSON *mntJson = cJSON_GetArrayItem(mountConfigs, i);
if (mntJson == NULL) {
continue;
}
PathMountNode *sandboxNode = DecodeMountPathConfig(mntJson, type);
APPSPAWN_CHECK_ONLY_EXPER(sandboxNode != NULL, continue);
AddSandboxMountNode(&sandboxNode->sandboxNode, section);
}
return 0;
}
static SymbolLinkNode *DecodeSymbolLinksConfig(const cJSON *config)
{
const char *target = GetStringFromJsonObj(config, "target-name");
const char *linkName = GetStringFromJsonObj(config, "link-name");
if (target == NULL || linkName == NULL) {
return NULL;
}
SymbolLinkNode *node = CreateSymbolLinkNode();
APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return NULL);
node->destMode = GetChmodFromJson(config);
node->checkErrorFlag = GetBoolValueFromJsonObj(config, "check-action-status", false);
node->target = strdup(target);
node->linkName = strdup(linkName);
if (node->target == NULL || node->linkName == NULL) {
APPSPAWN_LOGE("Failed to get sourc or target path");
DeleteSandboxMountNode((SandboxMountNode *)node);
return NULL;
}
return node;
}
static int ParseSymbolLinksConfig(AppSpawnSandboxCfg *sandbox,
const cJSON *symbolLinkConfigs, SandboxSection *section)
{
APPSPAWN_CHECK_ONLY_EXPER(symbolLinkConfigs != NULL && cJSON_IsArray(symbolLinkConfigs), return -1);
uint32_t symlinkPointSize = cJSON_GetArraySize(symbolLinkConfigs);
for (uint32_t i = 0; i < symlinkPointSize; i++) {
cJSON *symConfig = cJSON_GetArrayItem(symbolLinkConfigs, i);
if (symConfig == NULL) {
continue;
}
SymbolLinkNode *node = DecodeSymbolLinksConfig(symConfig);
APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return -1);
AddSandboxMountNode(&node->sandboxNode, section);
}
return 0;
}
static int ParseGidTableConfig(AppSpawnSandboxCfg *sandbox, const cJSON *configs, SandboxSection *section)
{
APPSPAWN_CHECK(cJSON_IsArray(configs), return 0, "json is not array.");
uint32_t arrayLen = (uint32_t)cJSON_GetArraySize(configs);
APPSPAWN_CHECK_ONLY_EXPER(arrayLen > 0, return 0);
APPSPAWN_CHECK(arrayLen < APP_MAX_GIDS, arrayLen = APP_MAX_GIDS, "More gid in gids json.");
section->gidTable = (gid_t *)calloc(1, sizeof(gid_t) * arrayLen);
APPSPAWN_CHECK(section->gidTable != NULL, return APPSPAWN_SYSTEM_ERROR, "Failed to alloc memory.");
for (uint32_t i = 0; i < arrayLen; i++) {
char *value = cJSON_GetStringValue(cJSON_GetArrayItem(configs, i));
gid_t gid = DecodeGid(value);
if (gid <= 0) {
continue;
}
section->gidTable[section->gidCount++] = gid;
}
return 0;
}
static int ParseMountGroupsConfig(AppSpawnSandboxCfg *sandbox, const cJSON *groupConfig, SandboxSection *section)
{
APPSPAWN_CHECK(cJSON_IsArray(groupConfig),
return APPSPAWN_SANDBOX_INVALID, "Invalid mount-groups config %{public}s", section->name);
uint32_t count = (uint32_t)cJSON_GetArraySize(groupConfig);
section->nameGroups = (SandboxMountNode **)malloc(sizeof(SandboxMountNode *) * count);
APPSPAWN_CHECK(section->nameGroups != NULL, return APPSPAWN_SYSTEM_ERROR, "Failed to alloc memory for group name");
SandboxNameGroupNode *mountNode = NULL;
for (uint32_t i = 0; i < count; i++) {
section->nameGroups[i] = NULL;
char *name = cJSON_GetStringValue(cJSON_GetArrayItem(groupConfig, i));
mountNode = (SandboxNameGroupNode *)GetSandboxSection(&sandbox->nameGroupsQueue, name);
if (mountNode == NULL) {
APPSPAWN_LOGE("Can not find name-group %{public}s", name);
continue;
}
// check type
if (strcmp(section->name, "system-const") == 0 && mountNode->destType != SANDBOX_TAG_SYSTEM_CONST) {
APPSPAWN_LOGE("Invalid name-group %{public}s", name);
continue;
}
section->nameGroups[section->number++] = (SandboxMountNode *)mountNode;
}
return 0;
}
static int ParseBaseConfig(AppSpawnSandboxCfg *sandbox, SandboxSection *section, const cJSON *configs)
{
APPSPAWN_CHECK_ONLY_EXPER(configs != NULL, return 0);
APPSPAWN_CHECK(cJSON_IsObject(configs),
return APPSPAWN_SANDBOX_INVALID, "Invalid config %{public}s", section->name);
APPSPAWN_LOGV("Parse sandbox %{public}s", section->name);
// "sandbox-switch": "ON", default sandbox switch is on
section->sandboxSwitch = GetBoolValueFromJsonObj(configs, "sandbox-switch", true);
// "sandbox-shared"
section->sandboxShared = GetBoolValueFromJsonObj(configs, "sandbox-shared", false);
int ret = 0;
cJSON *gidTabJson = cJSON_GetObjectItemCaseSensitive(configs, "gids");
if (gidTabJson) {
ret = ParseGidTableConfig(sandbox, gidTabJson, section);
APPSPAWN_CHECK(ret == 0, return ret, "Parse gids for %{public}s", section->name);
}
cJSON *pathConfigs = cJSON_GetObjectItemCaseSensitive(configs, "mount-paths");
if (pathConfigs != NULL) { // mount-paths
ret = ParseMountPathsConfig(sandbox, pathConfigs, section, SANDBOX_TAG_MOUNT_PATH);
APPSPAWN_CHECK(ret == 0, return ret, "Parse mount-paths for %{public}s", section->name);
}
pathConfigs = cJSON_GetObjectItemCaseSensitive(configs, "mount-files");
if (pathConfigs != NULL) { // mount-files
ret = ParseMountPathsConfig(sandbox, pathConfigs, section, SANDBOX_TAG_MOUNT_FILE);
APPSPAWN_CHECK(ret == 0, return ret, "Parse mount-paths for %{public}s", section->name);
}
pathConfigs = cJSON_GetObjectItemCaseSensitive(configs, "symbol-links");
if (pathConfigs != NULL) { // symbol-links
ret = ParseSymbolLinksConfig(sandbox, pathConfigs, section);
APPSPAWN_CHECK(ret == 0, return ret, "Parse symbol-links for %{public}s", section->name);
}
cJSON *groupConfig = cJSON_GetObjectItemCaseSensitive(configs, "mount-groups");
if (groupConfig != NULL) {
ret = ParseMountGroupsConfig(sandbox, groupConfig, section);
APPSPAWN_CHECK(ret == 0, return ret, "Parse mount-groups for %{public}s", section->name);
}
return 0;
}
static int ParsePackageNameConfig(AppSpawnSandboxCfg *sandbox, const char *name, const cJSON *packageNameConfigs)
{
APPSPAWN_LOGV("Parse package-name config %{public}s", name);
SandboxPackageNameNode *node = CreateSandboxPackageNameNode(name);
APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return -1);
int ret = ParseBaseConfig(sandbox, &node->section, packageNameConfigs);
if (ret != 0) {
DeleteSandboxSection((SandboxSection *)node);
return ret;
}
// success, insert section
AddSandboxSection(&node->section, &sandbox->packageNameQueue);
return 0;
}
static int ParseSpawnFlagsConfig(AppSpawnSandboxCfg *sandbox, const char *name, const cJSON *flagsConfig)
{
uint32_t flagIndex = GetFlagIndexFromJson(flagsConfig);
APPSPAWN_LOGV("Parse spawn-flags config %{public}s flagIndex %{public}u", name, flagIndex);
SandboxFlagsNode *node = CreateSandboxFlagsNode(name);
APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return -1);
node->flagIndex = flagIndex;
int ret = ParseBaseConfig(sandbox, &node->section, flagsConfig);
if (ret != 0) {
DeleteSandboxSection((SandboxSection *)node);
return ret;
}
// success, insert section
AddSandboxSection(&node->section, &sandbox->spawnFlagsQueue);
return 0;
}
static int ParsePermissionConfig(AppSpawnSandboxCfg *sandbox, const char *name, const cJSON *permissionConfig)
{
APPSPAWN_LOGV("Parse permission config %{public}s", name);
SandboxPermissionNode *node = CreateSandboxPermissionNode(name);
APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return -1);
int ret = ParseBaseConfig(sandbox, &node->section, permissionConfig);
if (ret != 0) {
DeleteSandboxSection((SandboxSection *)node);
return ret;
}
// success, insert section
AddSandboxSection(&node->section, &sandbox->permissionQueue);
return 0;
}
static SandboxNameGroupNode *ParseNameGroup(AppSpawnSandboxCfg *sandbox, const cJSON *groupConfig)
{
int ret = 0;
char *name = GetStringFromJsonObj(groupConfig, "name");
APPSPAWN_CHECK(name != NULL, return NULL, "No name in name group config");
APPSPAWN_LOGV("Parse name-group config %{public}s", name);
SandboxNameGroupNode *node = CreateSandboxNameGroupNode(name);
APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return NULL);
cJSON *obj = cJSON_GetObjectItemCaseSensitive(groupConfig, "mount-paths-deps");
if (obj) {
node->depNode = DecodeMountPathConfig(obj, SANDBOX_TAG_MOUNT_PATH);
if (node->depNode == NULL) {
DeleteSandboxSection((SandboxSection *)node);
return NULL;
}
// "mount-mode": "not-exists"
node->mountMode = GetMountModeFromConfig(obj, "mount-mode", MOUNT_MODE_ALWAYS);
}
ret = ParseBaseConfig(sandbox, &node->section, groupConfig);
if (ret != 0) {
DeleteSandboxSection((SandboxSection *)node);
return NULL;
}
// "type": "system-const",
// "caps": ["shared"],
node->destType = GetNameGroupTypeFromConfig(groupConfig, "type", SANDBOX_TAG_INVALID);
// success, insert section
AddSandboxSection(&node->section, &sandbox->nameGroupsQueue);
return node;
}
static int ParseNameGroupsConfig(AppSpawnSandboxCfg *sandbox, const cJSON *root)
{
APPSPAWN_CHECK(root != NULL, return APPSPAWN_SANDBOX_INVALID, "Invalid config ");
cJSON *configs = cJSON_GetObjectItemCaseSensitive(root, "name-groups");
APPSPAWN_CHECK_ONLY_EXPER(configs != NULL, return 0);
APPSPAWN_CHECK(cJSON_IsArray(configs), return APPSPAWN_SANDBOX_INVALID, "Invalid config ");
int count = cJSON_GetArraySize(configs);
APPSPAWN_CHECK_ONLY_EXPER(count > 0, return 0);
sandbox->depGroupNodes = (SandboxNameGroupNode **)calloc(1, sizeof(SandboxNameGroupNode *) * count);
APPSPAWN_CHECK(sandbox->depGroupNodes != NULL, return APPSPAWN_SYSTEM_ERROR, "Failed alloc memory ");
sandbox->depNodeCount = 0;
for (int i = 0; i < count; i++) {
cJSON *json = cJSON_GetArrayItem(configs, i);
if (json == NULL) {
continue;
}
SandboxNameGroupNode *node = ParseNameGroup(sandbox, json);
APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return APPSPAWN_SANDBOX_INVALID);
if (node->depNode) {
sandbox->depGroupNodes[sandbox->depNodeCount++] = node;
}
}
APPSPAWN_LOGV("ParseNameGroupsConfig depNodeCount %{public}d", sandbox->depNodeCount);
return 0;
}
static int ParseConditionalConfig(AppSpawnSandboxCfg *sandbox, const cJSON *configs, const char *configName,
int (*parseConfig)(AppSpawnSandboxCfg *sandbox, const char *name, const cJSON *configs))
{
APPSPAWN_CHECK_ONLY_EXPER(configs != NULL, return 0);
APPSPAWN_CHECK(cJSON_IsArray(configs),
return APPSPAWN_SANDBOX_INVALID, "Invalid config %{public}s", configName);
int ret = 0;
int count = cJSON_GetArraySize(configs);
for (int i = 0; i < count; i++) {
cJSON *json = cJSON_GetArrayItem(configs, i);
if (json == NULL) {
continue;
}
char *name = GetStringFromJsonObj(json, "name");
if (name == NULL) {
APPSPAWN_LOGE("No name in %{public}s configs", configName);
continue;
}
ret = parseConfig(sandbox, name, json);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
}
return 0;
}
static int ParseGlobalSandboxConfig(AppSpawnSandboxCfg *sandbox, const cJSON *root)
{
cJSON *json = cJSON_GetObjectItemCaseSensitive(root, "global");
if (json) {
sandbox->sandboxNsFlags = GetSandboxNsFlags(json);
char *rootPath = GetStringFromJsonObj(json, "sandbox-root");
APPSPAWN_CHECK(rootPath != NULL, return APPSPAWN_SYSTEM_ERROR, "No root path in config");
sandbox->rootPath = strdup(rootPath);
APPSPAWN_CHECK(sandbox->rootPath != NULL, return APPSPAWN_SYSTEM_ERROR, "Failed to copy root path");
sandbox->topSandboxSwitch = GetBoolValueFromJsonObj(json, "top-sandbox-switch", true);
}
return 0;
}
APPSPAWN_STATIC int ParseAppSandboxConfig(const cJSON *root, AppSpawnSandboxCfg *sandbox)
{
APPSPAWN_CHECK(root != NULL && sandbox, return APPSPAWN_SYSTEM_ERROR, "Invalid json");
int ret = ParseGlobalSandboxConfig(sandbox, root); // "global":
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return APPSPAWN_SANDBOX_INVALID);
ret = ParseNameGroupsConfig(sandbox, root); // name-groups
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return APPSPAWN_SANDBOX_INVALID);
// "required"
cJSON *required = cJSON_GetObjectItemCaseSensitive(root, "required");
if (required) {
cJSON *config = NULL;
cJSON_ArrayForEach(config, required) {
APPSPAWN_LOGI("Sandbox required config: %{public}s", config->string);
SandboxSection *section = CreateSandboxSection(config->string, sizeof(SandboxSection),
SANDBOX_TAG_REQUIRED);
APPSPAWN_CHECK_ONLY_EXPER(section != NULL, return -1);
int ret = ParseBaseConfig(sandbox, section, config);
if (ret != 0) {
DeleteSandboxSection(section);
return ret;
}
// success, insert section
AddSandboxSection(section, &sandbox->requiredQueue);
}
}
// conditional
cJSON *json = cJSON_GetObjectItemCaseSensitive(root, "conditional");
if (json != NULL) {
// permission
cJSON *config = cJSON_GetObjectItemCaseSensitive(json, "permission");
ret = ParseConditionalConfig(sandbox, config, "permission", ParsePermissionConfig);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
// spawn-flag
config = cJSON_GetObjectItemCaseSensitive(json, "spawn-flag");
ret = ParseConditionalConfig(sandbox, config, "spawn-flag", ParseSpawnFlagsConfig);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
// package-name
config = cJSON_GetObjectItemCaseSensitive(json, "package-name");
ret = ParseConditionalConfig(sandbox, config, "package-name", ParsePackageNameConfig);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
}
return ret;
}
int LoadAppSandboxConfig(AppSpawnSandboxCfg *sandbox, int nwebSpawn)
{
APPSPAWN_CHECK_ONLY_EXPER(sandbox != NULL, return APPSPAWN_ARG_INVALID);
const char *sandboxName = nwebSpawn ? WEB_SANDBOX_FILE_NAME : APP_SANDBOX_FILE_NAME;
int ret = ParseSandboxConfig("etc/sandbox", sandboxName, ParseAppSandboxConfig, sandbox);
if (ret == APPSPAWN_SANDBOX_NONE) {
APPSPAWN_LOGW("No sandbox config");
ret = 0;
}
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
sandbox->pidNamespaceSupport = AppSandboxPidNsIsSupport();
sandbox->appFullMountEnable = CheckAppFullMountEnable();
APPSPAWN_LOGI("Sandbox pidNamespaceSupport: %{public}d appFullMountEnable: %{public}d",
sandbox->pidNamespaceSupport, sandbox->appFullMountEnable);
return 0;
}

544
modules/sandbox/sandbox_manager.c Executable file
View File

@ -0,0 +1,544 @@
/*
* 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.
*/
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <sched.h>
#include "appspawn_permission.h"
#include "appspawn_sandbox.h"
#include "appspawn_utils.h"
#include "modulemgr.h"
#include "parameter.h"
#include "securec.h"
static void FreePathMountNode(SandboxMountNode *node)
{
PathMountNode *sandboxNode = (PathMountNode *)node;
if (sandboxNode->source) {
free(sandboxNode->source);
sandboxNode->source = NULL;
}
if (sandboxNode->target) {
free(sandboxNode->target);
sandboxNode->target = NULL;
}
if (sandboxNode->appAplName) {
free(sandboxNode->appAplName);
sandboxNode->appAplName = NULL;
}
free(sandboxNode);
}
static void FreeSymbolLinkNode(SandboxMountNode *node)
{
SymbolLinkNode *sandboxNode = (SymbolLinkNode *)node;
if (sandboxNode->target) {
free(sandboxNode->target);
sandboxNode->target = NULL;
}
if (sandboxNode->linkName) {
free(sandboxNode->linkName);
sandboxNode->linkName = NULL;
}
free(sandboxNode);
}
static int SandboxNodeCompareProc(ListNode *node, ListNode *newNode)
{
SandboxMountNode *sandbox1 = (SandboxMountNode *)ListEntry(node, SandboxMountNode, node);
SandboxMountNode *sandbox2 = (SandboxMountNode *)ListEntry(newNode, SandboxMountNode, node);
return sandbox1->type - sandbox2->type;
}
SandboxMountNode *CreateSandboxMountNode(uint32_t dataLen, uint32_t type)
{
APPSPAWN_CHECK(dataLen >= sizeof(SandboxMountNode) && dataLen <= sizeof(PathMountNode),
return NULL, "Invalid dataLen %{public}u", dataLen);
SandboxMountNode *node = (SandboxMountNode *)calloc(1, dataLen);
APPSPAWN_CHECK(node != NULL, return NULL, "Failed to create mount node %{public}u", type);
OH_ListInit(&node->node);
node->type = type;
return node;
}
void AddSandboxMountNode(SandboxMountNode *node, SandboxSection *queue)
{
OH_ListAddWithOrder(&queue->front, &node->node, SandboxNodeCompareProc);
}
void DeleteSandboxMountNode(SandboxMountNode *sandboxNode)
{
switch (sandboxNode->type) {
case SANDBOX_TAG_MOUNT_PATH:
case SANDBOX_TAG_MOUNT_FILE:
FreePathMountNode(sandboxNode);
break;
case SANDBOX_TAG_SYMLINK:
FreeSymbolLinkNode(sandboxNode);
break;
default:
APPSPAWN_LOGE("Invalid type %{public}u", sandboxNode->type);
free(sandboxNode);
break;
}
}
SandboxMountNode *GetFirstSandboxMountNode(const SandboxSection *section)
{
if (ListEmpty(section->front)) {
return NULL;
}
return (SandboxMountNode *)ListEntry(section->front.next, SandboxMountNode, node);
}
void DumpSandboxMountNode(const SandboxMountNode *sandboxNode, uint32_t index)
{
switch (sandboxNode->type) {
case SANDBOX_TAG_MOUNT_PATH:
case SANDBOX_TAG_MOUNT_FILE: {
PathMountNode *pathNode = (PathMountNode *)sandboxNode;
APPSPAPWN_DUMP(" ****************************** %{public}u", index);
APPSPAPWN_DUMP(" sandbox node source: %{public}s", pathNode->source ? pathNode->source : "null");
APPSPAPWN_DUMP(" sandbox node target: %{public}s", pathNode->target ? pathNode->target : "null");
DumpMountPathMountNode(pathNode);
APPSPAPWN_DUMP(" sandbox node apl: %{public}s",
pathNode->appAplName ? pathNode->appAplName : "null");
APPSPAPWN_DUMP(" sandbox node checkErrorFlag: %{public}s",
pathNode->checkErrorFlag ? "true" : "false");
break;
}
case SANDBOX_TAG_SYMLINK: {
SymbolLinkNode *linkNode = (SymbolLinkNode *)sandboxNode;
APPSPAPWN_DUMP(" ***********************************");
APPSPAPWN_DUMP(" sandbox node target: %{public}s", linkNode->target ? linkNode->target : "null");
APPSPAPWN_DUMP(" sandbox node linkName: %{public}s",
linkNode->linkName ? linkNode->linkName : "null");
APPSPAPWN_DUMP(" sandbox node destMode: %{public}x", linkNode->destMode);
APPSPAPWN_DUMP(" sandbox node checkErrorFlag: %{public}s",
linkNode->checkErrorFlag ? "true" : "false");
break;
}
default:
break;
}
}
static inline void InitSandboxSection(SandboxSection *section, int type)
{
OH_ListInit(&section->front);
section->sandboxSwitch = 0;
section->sandboxShared = 0;
section->number = 0;
section->gidCount = 0;
section->gidTable = NULL;
section->nameGroups = NULL;
section->name = NULL;
OH_ListInit(&section->sandboxNode.node);
section->sandboxNode.type = type;
}
static void ClearSandboxSection(SandboxSection *section)
{
if (section->gidTable) {
free(section->gidTable);
section->gidTable = NULL;
}
// free name group
if (section->nameGroups) {
free(section->nameGroups);
section->nameGroups = NULL;
}
// free mount path
ListNode *node = section->front.next;
while (node != &section->front) {
SandboxMountNode *sandboxNode = ListEntry(node, SandboxMountNode, node);
// delete node
OH_ListRemove(&sandboxNode->node);
OH_ListInit(&sandboxNode->node);
DeleteSandboxMountNode(sandboxNode);
// get next
node = section->front.next;
}
}
static void DumpSandboxQueue(const ListNode *front,
void (*dumpSandboxMountNode)(const SandboxMountNode *node, uint32_t count))
{
uint32_t count = 0;
ListNode *node = front->next;
while (node != front) {
SandboxMountNode *sandboxNode = (SandboxMountNode *)ListEntry(node, SandboxMountNode, node);
count++;
dumpSandboxMountNode(sandboxNode, count);
// get next
node = node->next;
}
}
static void DumpSandboxSection(const SandboxSection *section)
{
APPSPAPWN_DUMP(" ========================================= ");
APPSPAPWN_DUMP(" Section %{public}s", section->name);
APPSPAPWN_DUMP(" sandboxSwitch %{public}s", section->sandboxSwitch ? "true" : "false");
APPSPAPWN_DUMP(" sandboxShared %{public}s", section->sandboxShared ? "true" : "false");
APPSPAPWN_DUMP(" gidCount: %{public}u", section->gidCount);
for (uint32_t index = 0; index < section->gidCount; index++) {
APPSPAPWN_DUMP(" gidTable[%{public}u]: %{public}u", index, section->gidTable[index]);
}
APPSPAPWN_DUMP(" mount group count: %{public}u", section->number);
for (uint32_t i = 0; i < section->number; i++) {
if (section->nameGroups[i]) {
SandboxNameGroupNode *groupNode = (SandboxNameGroupNode *)section->nameGroups[i];
APPSPAPWN_DUMP(" name[%{public}d] %{public}s", i, groupNode->section.name);
}
}
APPSPAPWN_DUMP(" mount-paths: ");
DumpSandboxQueue(&section->front, DumpSandboxMountNode);
}
SandboxSection *CreateSandboxSection(const char *name, uint32_t dataLen, uint32_t type)
{
APPSPAWN_CHECK(name != NULL, return NULL, "Invalid name %{public}u", type);
APPSPAWN_CHECK(dataLen >= sizeof(SandboxSection), return NULL, "Invalid dataLen %{public}u", dataLen);
APPSPAWN_CHECK(dataLen <= sizeof(SandboxNameGroupNode), return NULL, "Invalid dataLen %{public}u", dataLen);
SandboxSection *section = (SandboxSection *)calloc(1, dataLen);
APPSPAWN_CHECK(section != NULL, return NULL, "Failed to create base node");
InitSandboxSection(section, type);
section->name = strdup(name);
if (section->name == NULL) {
ClearSandboxSection(section);
free(section);
return NULL;
}
return section;
}
static int SandboxConditionalNodeCompareName(ListNode *node, void *data)
{
SandboxSection *tmpNode = (SandboxSection *)ListEntry(node, SandboxMountNode, node);
return strcmp(tmpNode->name, (char *)data);
}
static int SandboxConditionalNodeCompareNode(ListNode *node, ListNode *newNode)
{
SandboxSection *tmpNode = (SandboxSection *)ListEntry(node, SandboxMountNode, node);
SandboxSection *tmpNewNode = (SandboxSection *)ListEntry(newNode, SandboxMountNode, node);
return strcmp(tmpNode->name, tmpNewNode->name);
}
SandboxSection *GetSandboxSection(const SandboxQueue *queue, const char *name)
{
APPSPAWN_CHECK_ONLY_EXPER(name != NULL && queue != NULL, return NULL);
ListNode *node = OH_ListFind(&queue->front, (void *)name, SandboxConditionalNodeCompareName);
if (node == NULL) {
return NULL;
}
return (SandboxSection *)ListEntry(node, SandboxMountNode, node);
}
void AddSandboxSection(SandboxSection *node, SandboxQueue *queue)
{
APPSPAWN_CHECK_ONLY_EXPER(node != NULL && queue != NULL, return);
OH_ListAddWithOrder(&queue->front, &node->sandboxNode.node, SandboxConditionalNodeCompareNode);
}
void DeleteSandboxSection(SandboxSection *section)
{
APPSPAWN_CHECK_ONLY_EXPER(section != NULL, return);
// delete node
OH_ListRemove(&section->sandboxNode.node);
OH_ListInit(&section->sandboxNode.node);
ClearSandboxSection(section);
free(section);
}
static void SandboxQueueClear(SandboxQueue *queue)
{
ListNode *node = queue->front.next;
while (node != &queue->front) {
SandboxSection *sandboxNode = (SandboxSection *)ListEntry(node, SandboxMountNode, node);
DeleteSandboxSection(sandboxNode);
// get first
node = queue->front.next;
}
}
static int AppSpawnExtDataCompareDataId(ListNode *node, void *data)
{
AppSpawnExtData *extData = (AppSpawnExtData *)ListEntry(node, AppSpawnExtData, node);
return extData->dataId - *(uint32_t *)data;
}
AppSpawnSandboxCfg *GetAppSpawnSandbox(const AppSpawnMgr *content)
{
APPSPAWN_CHECK_ONLY_EXPER(content != NULL, return NULL);
uint32_t dataId = EXT_DATA_SANDBOX;
ListNode *node = OH_ListFind(&content->extData, (void *)&dataId, AppSpawnExtDataCompareDataId);
if (node == NULL) {
return NULL;
}
return (AppSpawnSandboxCfg *)ListEntry(node, AppSpawnSandboxCfg, extData);
}
void DeleteAppSpawnSandbox(AppSpawnSandboxCfg *sandbox)
{
APPSPAWN_CHECK_ONLY_EXPER(sandbox != NULL, return);
APPSPAWN_LOGV("DeleteAppSpawnSandbox");
OH_ListRemove(&sandbox->extData.node);
OH_ListInit(&sandbox->extData.node);
// delete all queue
SandboxQueueClear(&sandbox->requiredQueue);
SandboxQueueClear(&sandbox->permissionQueue);
SandboxQueueClear(&sandbox->packageNameQueue);
SandboxQueueClear(&sandbox->spawnFlagsQueue);
SandboxQueueClear(&sandbox->nameGroupsQueue);
free(sandbox->depGroupNodes);
sandbox->depGroupNodes = NULL;
free(sandbox);
sandbox = NULL;
}
static void DumpSandboxSectionNode(const SandboxMountNode *node, uint32_t index)
{
SandboxPermissionNode *permissionNode = (SandboxPermissionNode *)node;
DumpSandboxSection(&permissionNode->section);
}
static void DumpSandbox(struct TagAppSpawnExtData *data)
{
AppSpawnSandboxCfg *sandbox = (AppSpawnSandboxCfg *)data;
DumpAppSpawnSandboxCfg(sandbox);
}
static inline void InitSandboxQueue(SandboxQueue *queue, uint32_t type)
{
OH_ListInit(&queue->front);
queue->type = type;
}
static void FreeAppSpawnSandbox(struct TagAppSpawnExtData *data)
{
AppSpawnSandboxCfg *sandbox = ListEntry(data, AppSpawnSandboxCfg, extData);
UnmountSandboxConfigs(sandbox);
// delete all var
DeleteAppSpawnSandbox(sandbox);
}
static void ClearAppSpawnSandbox(struct TagAppSpawnExtData *data)
{
// clear no use sand box config
}
AppSpawnSandboxCfg *CreateAppSpawnSandbox(void)
{
// create sandbox
AppSpawnSandboxCfg *sandbox = (AppSpawnSandboxCfg *)calloc(1, sizeof(AppSpawnSandboxCfg));
APPSPAWN_CHECK(sandbox != NULL, return NULL, "Failed to create sandbox");
// ext data init
OH_ListInit(&sandbox->extData.node);
sandbox->extData.dataId = EXT_DATA_SANDBOX;
sandbox->extData.freeNode = FreeAppSpawnSandbox;
sandbox->extData.clearNode = ClearAppSpawnSandbox;
sandbox->extData.dumpNode = DumpSandbox;
// queue
InitSandboxQueue(&sandbox->requiredQueue, SANDBOX_TAG_REQUIRED);
InitSandboxQueue(&sandbox->permissionQueue, SANDBOX_TAG_PERMISSION);
InitSandboxQueue(&sandbox->packageNameQueue, SANDBOX_TAG_PACKAGE_NAME);
InitSandboxQueue(&sandbox->spawnFlagsQueue, SANDBOX_TAG_SPAWN_FLAGS);
InitSandboxQueue(&sandbox->nameGroupsQueue, SANDBOX_TAG_NAME_GROUP);
sandbox->topSandboxSwitch = 0;
sandbox->appFullMountEnable = 0;
sandbox->topSandboxSwitch = 0;
sandbox->pidNamespaceSupport = 0;
sandbox->sandboxNsFlags = 0;
sandbox->maxPermissionIndex = -1;
sandbox->depNodeCount = 0;
sandbox->depGroupNodes = NULL;
for (uint32_t i = 0; i < ARRAY_LENGTH(sandbox->systemUids); i++) {
sandbox->systemUids[i] = INVALID_UID;
}
AddDefaultVariable();
AddDefaultExpandAppSandboxConfigHandle();
return sandbox;
}
void DumpAppSpawnSandboxCfg(AppSpawnSandboxCfg *sandbox)
{
APPSPAWN_CHECK_ONLY_EXPER(sandbox != NULL, return);
APPSPAPWN_DUMP("Sandbox sandboxNsFlags: %{public}x ", sandbox->sandboxNsFlags);
APPSPAPWN_DUMP("Sandbox topSandboxSwitch: %{public}s", sandbox->topSandboxSwitch ? "true" : "false");
APPSPAPWN_DUMP("Sandbox appFullMountEnable: %{public}s", sandbox->appFullMountEnable ? "true" : "false");
APPSPAPWN_DUMP("Sandbox pidNamespaceSupport: %{public}s", sandbox->pidNamespaceSupport ? "true" : "false");
APPSPAPWN_DUMP("Sandbox common info: ");
DumpSandboxQueue(&sandbox->requiredQueue.front, DumpSandboxSectionNode);
DumpSandboxQueue(&sandbox->packageNameQueue.front, DumpSandboxSectionNode);
DumpSandboxQueue(&sandbox->permissionQueue.front, DumpSandboxSectionNode);
DumpSandboxQueue(&sandbox->spawnFlagsQueue.front, DumpSandboxSectionNode);
DumpSandboxQueue(&sandbox->nameGroupsQueue.front, DumpSandboxSectionNode);
}
static int PreLoadSandboxCfg(AppSpawnMgr *content)
{
AppSpawnSandboxCfg *sandbox = GetAppSpawnSandbox(content);
APPSPAWN_CHECK(sandbox == NULL, return 0, "Sandbox has been load");
sandbox = CreateAppSpawnSandbox();
APPSPAWN_CHECK_ONLY_EXPER(sandbox != NULL, return APPSPAWN_SYSTEM_ERROR);
OH_ListAddTail(&sandbox->extData.node, &content->extData);
// load app sandbox config
LoadAppSandboxConfig(sandbox, IsNWebSpawnMode(content));
sandbox->maxPermissionIndex = PermissionRenumber(&sandbox->permissionQueue);
content->content.sandboxNsFlags = 0;
if (IsNWebSpawnMode(content) || sandbox->pidNamespaceSupport) {
content->content.sandboxNsFlags = sandbox->sandboxNsFlags;
}
return 0;
}
int SpawnBuildSandboxEnv(AppSpawnMgr *content, AppSpawningCtx *property)
{
AppSpawnSandboxCfg *appSandbox = GetAppSpawnSandbox(content);
APPSPAWN_CHECK(appSandbox != NULL, return -1, "Failed to get sandbox for %{public}s", GetProcessName(property));
// CLONE_NEWPID 0x20000000
// CLONE_NEWNET 0x40000000
if ((content->content.sandboxNsFlags & CLONE_NEWPID) == CLONE_NEWPID) {
int ret = getprocpid();
if (ret < 0) {
return ret;
}
}
int ret = MountSandboxConfigs(appSandbox, property, IsNWebSpawnMode(content));
// for module test do not create sandbox
if (strncmp(GetBundleName(property), MODULE_TEST_BUNDLE_NAME, strlen(MODULE_TEST_BUNDLE_NAME)) == 0) {
return 0;
}
return ret == 0 ? 0 : APPSPAWN_SANDBOX_MOUNT_FAIL;
}
static int AppendPermissionGid(const AppSpawnSandboxCfg *sandbox, 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_DAC_INFO, GetProcessName(property));
ListNode *node = sandbox->permissionQueue.front.next;
while (node != &sandbox->permissionQueue.front) {
SandboxPermissionNode *permissionNode = (SandboxPermissionNode *)ListEntry(node, SandboxMountNode, node);
if (!CheckAppPermissionFlagSet(property, (uint32_t)permissionNode->permissionIndex)) {
node = node->next;
continue;
}
if (permissionNode->section.gidCount == 0) {
node = node->next;
continue;
}
APPSPAWN_LOGV("Append permission gid %{public}s to %{public}s permission: %{public}u message: %{public}u",
permissionNode->section.name, GetProcessName(property), permissionNode->section.gidCount,
dacInfo->gidCount);
size_t copyLen = permissionNode->section.gidCount;
if ((permissionNode->section.gidCount + dacInfo->gidCount) > APP_MAX_GIDS) {
APPSPAWN_LOGW("More gid for %{public}s msg count %{public}u permission %{public}u",
GetProcessName(property), dacInfo->gidCount, permissionNode->section.gidCount);
copyLen = APP_MAX_GIDS - dacInfo->gidCount;
}
int ret = memcpy_s(&dacInfo->gidTable[dacInfo->gidCount], sizeof(gid_t) * copyLen,
permissionNode->section.gidTable, sizeof(gid_t) * copyLen);
if (ret != EOK) {
APPSPAWN_LOGW("Failed to append permission %{public}s gid to %{public}s",
permissionNode->section.name, GetProcessName(property));
}
node = node->next;
}
return 0;
}
static bool IsSystemConstMounted(const AppSpawnSandboxCfg *sandbox, AppSpawnMsgDacInfo *info)
{
uid_t uid = info->uid / UID_BASE;
for (uint32_t i = 0; i < ARRAY_LENGTH(sandbox->systemUids); i++) {
if (sandbox->systemUids[i] == uid) {
return true;
}
}
return false;
}
static int SetSystemConstMounted(AppSpawnSandboxCfg *sandbox, AppSpawnMsgDacInfo *info)
{
uid_t uid = info->uid / UID_BASE;
for (uint32_t i = 0; i < ARRAY_LENGTH(sandbox->systemUids); i++) {
if (sandbox->systemUids[i] == INVALID_UID) {
sandbox->systemUids[i] = uid;
break;
}
}
return 0;
}
int SpawnPrepareSandboxCfg(AppSpawnMgr *content, AppSpawningCtx *property)
{
APPSPAWN_CHECK_ONLY_EXPER(content != NULL, return -1);
APPSPAWN_CHECK_ONLY_EXPER(property != NULL, return -1);
APPSPAWN_LOGV("Prepare sandbox config %{public}s", GetProcessName(property));
AppSpawnSandboxCfg *sandbox = GetAppSpawnSandbox(content);
APPSPAWN_CHECK(sandbox != NULL, return -1, "Failed to get sandbox for %{public}s", GetProcessName(property));
if (sandbox->appFullMountEnable) {
int index = GetPermissionIndexInQueue(&sandbox->permissionQueue, FILE_CROSS_APP_MODE);
if (index > 0) {
SetAppPermissionFlags(property, index);
}
}
int ret = AppendPermissionGid(sandbox, property);
APPSPAWN_CHECK(ret == 0, return ret, "Failed to add gid for %{public}s", GetProcessName(property));
AppSpawnMsgDacInfo *info = (AppSpawnMsgDacInfo *)GetAppProperty(property, TLV_DAC_INFO);
APPSPAWN_CHECK(info != NULL, return APPSPAWN_TLV_NONE,
"No tlv %{public}d in msg %{public}s", TLV_DAC_INFO, GetProcessName(property));
if (!IsSystemConstMounted(sandbox, info)) {
ret = StagedMountSystemConst(sandbox, property, IsNWebSpawnMode(content));
APPSPAWN_CHECK(ret == 0, return ret, "Failed to mount system-const for %{public}s", GetProcessName(property));
SetSystemConstMounted(sandbox, info);
} else {
APPSPAWN_LOGW("System-const config [uid: %{public}u] has been set", info->uid / UID_BASE);
}
return 0;
}
MODULE_CONSTRUCTOR(void)
{
APPSPAWN_LOGV("Load sandbox module ...");
AddPreloadHook(HOOK_PRIO_SANDBOX, PreLoadSandboxCfg);
AddAppSpawnHook(STAGE_PARENT_PRE_FORK, HOOK_PRIO_SANDBOX, SpawnPrepareSandboxCfg);
AddAppSpawnHook(STAGE_CHILD_EXECUTE, HOOK_PRIO_SANDBOX, SpawnBuildSandboxEnv);
}
MODULE_DESTRUCTOR(void)
{
ClearVariable();
ClearExpandAppSandboxConfigHandle();
}

38
modules/sysevent/BUILD.gn Executable file
View File

@ -0,0 +1,38 @@
# 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.
import("//base/startup/appspawn/appspawn.gni")
import("//build/ohos.gni")
ohos_shared_library("event_reporter") {
sources = [ "event_reporter.cpp" ]
include_dirs = [
".",
"${appspawn_path}/common",
"${appspawn_path}/standard",
]
external_deps = [
"hilog:libhilog",
"hisysevent:libhisysevent",
"init:libbegetutil",
]
deps = [ "${appspawn_path}/modules/module_engine:libappspawn_module_engine" ]
subsystem_name = "${subsystem_name}"
part_name = "${part_name}"
install_enable = true
if (target_cpu == "arm64" || target_cpu == "x86_64" ||
target_cpu == "riscv64") {
module_install_dir = "lib64/appspawn/common"
} else {
module_install_dir = "lib/appspawn/common"
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2022 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 <hisysevent.h>
#include "appspawn_hook.h"
#include "appspawn_manager.h"
#include "appspawn_utils.h"
using namespace OHOS::HiviewDFX;
namespace OHOS {
namespace system {
constexpr char KEY_PROCESS_EXIT[] = "PROCESS_EXIT";
constexpr char KEY_NAME[] = "PROCESS_NAME";
constexpr char KEY_PID[] = "PID";
constexpr char KEY_UID[] = "UID";
constexpr char KEY_STATUS[] = "STATUS";
constexpr int32_t MAX_NAME_LENGTH = 1024;
void ProcessMgrRemoveApp(const char* processName, int pid, int uid, int status)
{
std::string pname = "Unknown";
if ((processName != NULL) && (strlen(processName) <= MAX_NAME_LENGTH)) {
pname = std::string(processName, strlen(processName));
}
HiSysEventWrite(HiSysEvent::Domain::STARTUP, KEY_PROCESS_EXIT, HiSysEvent::EventType::BEHAVIOR,
KEY_NAME, pname, KEY_PID, pid, KEY_UID, uid, KEY_STATUS, status);
}
} // namespace system
} // namespace OHOS
static int ProcessMgrRemoveApp(const AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo)
{
OHOS::system::ProcessMgrRemoveApp(appInfo->name, appInfo->pid, appInfo->uid, appInfo->exitStatus);
return 0;
}
MODULE_CONSTRUCTOR(void)
{
APPSPAWN_LOGV("Load sys event module ...");
AddProcessMgrHook(STAGE_SERVER_APP_DIED, 0, ProcessMgrRemoveApp);
}

View File

@ -22,7 +22,7 @@
#include "interfaces/innerkits/include/appspawn_msg.h"
#include "interfaces/innerkits_new/include/appspawn.h"
#include "interfaces/innerkits_new/module_engine/include/appspawn_msg.h"
#include "modules/module_engine/include/appspawn_msg.h"
#include "appspawn_service.h"
#include "appspawn_utils.h"
#include "securec.h"

View File

@ -32,6 +32,7 @@
#include <sys/wait.h>
#include <unistd.h>
#include <sched.h>
#include <dirent.h>
#include "securec.h"
#include "selinux/selinux.h"
@ -605,6 +606,79 @@ int GetAppSpawnClientFromArg(int argc, char *const argv[], AppSpawnClientExt *cl
return ret;
}
static pid_t GetPidByName(const char *name)
{
int pid = -1; // initial pid set to -1
DIR *dir = opendir("/proc");
if (dir == NULL) {
return -1;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type != DT_DIR) {
continue;
}
long pidNum = strtol(entry->d_name, NULL, 10); // pid will not exceed a 10-digit decimal number
if (pidNum <= 0) {
continue;
}
char path[32]; // path that contains the process name
if (snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%s/comm", entry->d_name) < 0) {
continue;
}
FILE *file = fopen(path, "r");
if (file == NULL) {
continue;
}
char buffer[32]; // read the process name
if (fgets(buffer, sizeof(buffer), file) == NULL) {
(void)fclose(file);
continue;
}
buffer[strcspn(buffer, "\n")] = 0;
if (strcmp(buffer, name) != 0) {
(void)fclose(file);
continue;
}
APPSPAWN_LOGI("get pid of %{public}s success", name);
pid = (int)pidNum;
(void)fclose(file);
break;
}
closedir(dir);
return pid;
}
static int NsInitFunc()
{
setuid(PID_NS_INIT_UID);
setgid(PID_NS_INIT_GID);
setcon("u:r:pid_ns_init:s0");
char* argv[] = {"/system/bin/pid_ns_init", NULL};
execve("/system/bin/pid_ns_init", argv, NULL);
return 0;
}
static int GetNsPidFd(pid_t pid)
{
char nsPath[256]; // filepath of ns pid
int ret = snprintf_s(nsPath, sizeof(nsPath), sizeof(nsPath) - 1, "/proc/%d/ns/pid", pid);
if (ret < 0) {
APPSPAWN_LOGE("SetPidNamespace failed, snprintf_s error:%{public}s", strerror(errno));
return -1;
}
int nsFd = open(nsPath, O_RDONLY);
if (nsFd < 0) {
APPSPAWN_LOGE("open ns pid:%{public}d failed, err:%{public}s", pid, strerror(errno));
return -1;
}
return nsFd;
}
static int EnablePidNs(AppSpawnContent *content)
{
AppSpawnContentExt *appSpawnContent = (AppSpawnContentExt *)content;
@ -616,16 +690,30 @@ static int EnablePidNs(AppSpawnContent *content)
return 0;
}
int ret = unshare(CLONE_NEWPID);
APPSPAWN_CHECK(ret == 0, return -1, "unshare CLONE_NWEPID failed, errno=%{public}d", errno);
// check if process pid_ns_init exists, this is the init process for pid namespace
pid_t pid = GetPidByName("pid_ns_init");
if (pid == -1) {
APPSPAWN_LOGI("Start Create pid_ns_init");
pid = clone(NsInitFunc, NULL, CLONE_NEWPID, NULL);
if (pid < 0) {
APPSPAWN_LOGE("clone pid ns init failed");
return -1;
}
} else {
APPSPAWN_LOGI("pid_ns_init exists, no need to create");
}
pid_t pid = fork();
if (pid == 0) {
setuid(PID_NS_INIT_UID);
setgid(PID_NS_INIT_GID);
setcon("u:r:pid_ns_init:s0");
char* argv[] = {"/system/bin/pid_ns_init", NULL};
execve("/system/bin/pid_ns_init", argv, NULL);
content->nsSelfPidFd = GetNsPidFd(getpid());
if (content->nsSelfPidFd < 0) {
APPSPAWN_LOGE("open ns pid of appspawn fail");
return -1;
}
content->nsInitPidFd = GetNsPidFd(pid);
if (content->nsInitPidFd < 0) {
APPSPAWN_LOGE("open ns pid of pid_ns_init fail");
close(content->nsSelfPidFd);
return -1;
}
APPSPAWN_LOGI("Enable pid namespace success.");

View File

@ -702,6 +702,12 @@ static void AppSpawnRun(AppSpawnContent *content, int argc, char *const argv[])
}
LE_CloseSignalTask(LE_GetDefaultLoop(), appSpawnContent->sigHandler);
// release resource
if (content->nsInitPidFd > 0) {
close(content->nsInitPidFd);
}
if (content->nsSelfPidFd > 0) {
close(content->nsSelfPidFd);
}
OH_HashMapDestory(appSpawnContent->appMap, NULL);
LE_CloseStreamTask(LE_GetDefaultLoop(), appSpawnContent->server);
LE_CloseLoop(LE_GetDefaultLoop());

View File

@ -20,7 +20,7 @@
#include <stdbool.h>
#include "interfaces/innerkits/include/appspawn_msg.h"
#include "interfaces/innerkits_new/include/appspawn.h"
#include "interfaces/innerkits_new/module_engine/include/appspawn_msg.h"
#include "modules/module_engine/include/appspawn_msg.h"
#include "appspawn_server.h"
#include "init_hashmap.h"
#include "loop_event.h"

View File

@ -29,6 +29,7 @@ ohos_executable("AppSpawnTest") {
"${appspawn_path}/modules/ace_adapter",
"${appspawn_path}/modules/common",
"${appspawn_path}/modules/sandbox",
"${appspawn_path}/modules//module_engine//include",
"${appspawn_path}/test/moduletest/threadpool",
"${appspawn_innerkits_path}/include",
"${appspawn_innerkits_path}/client",
@ -37,6 +38,7 @@ ohos_executable("AppSpawnTest") {
"${appspawn_path}/test/mock",
"${appspawn_path}/test/unittest",
"${appspawn_path}/util/include",
"${appspawn_path}/modules/module_engine/include",
]
deps = [
@ -82,6 +84,7 @@ ohos_moduletest("AppSpawnModuleTest") {
"${appspawn_path}/test/mock",
"${appspawn_path}/test/unittest",
"${appspawn_path}/util/include",
"${appspawn_path}/modules/module_engine/include",
]
deps = [

View File

@ -34,6 +34,7 @@ ohos_unittest("AppSpawn_client_ut") {
"waitpid=WaitpidStub",
"HapContext=HapContextStub",
"APPSPAWN_TEST",
"APPSPAWN_CLIENT",
"OHOS_DEBUG",
"GRAPHIC_PERMISSION_CHECK",
]
@ -45,11 +46,13 @@ ohos_unittest("AppSpawn_client_ut") {
"${appspawn_path}/adapter",
"${appspawn_path}/adapter/sysevent",
"${appspawn_path}/modules/sandbox",
"${appspawn_path}/modules//module_engine//include",
"${appspawn_path}/interfaces/innerkits_new/include",
"${appspawn_path}/interfaces/innerkits_new/client",
"${appspawn_path}/interfaces/innerkits_new/permission",
"${appspawn_path}/util/include",
"${appspawn_path}/interfaces/innerkits_new/module_engine/include",
"${appspawn_path}/modules/module_engine/include",
"//third_party/json/include",
]
sources = [