feat: 应用沙箱增强,使能pid namespace

Close #I5OE8Q

Signed-off-by: zhushengle <zhushengle@huawei.com>
Change-Id: I2897c0c9aacff3a06212d27956e3cb46ea03799e
This commit is contained in:
zhushengle 2022-08-29 22:34:04 +08:00
parent c41d515381
commit 4f35b367da
13 changed files with 237 additions and 59 deletions

View File

@ -34,6 +34,7 @@ void RecordRenderProcessExitedStatus(pid_t pid, int status);
void LoadAppSandboxConfig(void);
void SetUidGidFilter(struct AppSpawnContent_ *content);
int SetSeccompFilter(struct AppSpawnContent_ *content, AppSpawnClient *client);
int32_t GetAppNamespaceFlags(const char *bundleName);
#ifdef __cplusplus
}

View File

@ -25,7 +25,8 @@ using namespace OHOS;
using namespace OHOS::AppSpawn;
namespace {
const std::string MODULE_TEST_BUNDLE_NAME("moduleTestProcessName");
const std::string MODULE_TEST_BUNDLE_NAME("moduleTestProcessName");
const std::string NAMESPACE_JSON_CONFIG("/system/etc/sandbox/sandbox-config.json");
#ifdef __aarch64__
const std::string APP_JSON_CONFIG("/system/etc/sandbox/appdata-sandbox64.json");
#else
@ -45,12 +46,18 @@ void LoadAppSandboxConfig(void)
rc = JsonUtils::GetJsonObjFromJson(appSandboxConfig, PRODUCT_JSON_CONFIG);
APPSPAWN_CHECK_ONLY_LOG(rc, "AppSpawnServer::Failed to load app product sandbox config");
SandboxUtils::StoreProductJsonConfig(appSandboxConfig);
nlohmann::json appNamespaceConfig;
rc = JsonUtils::GetJsonObjFromJson(appNamespaceConfig, NAMESPACE_JSON_CONFIG);
APPSPAWN_CHECK_ONLY_LOG(rc, "AppSpawnServer::Failed to load app sandbox namespace config");
SandboxUtils::StoreNamespaceJsonConfig(appNamespaceConfig);
}
int32_t SetAppSandboxProperty(struct AppSpawnContent_ *content, AppSpawnClient *client)
{
APPSPAWN_CHECK(client != NULL, return -1, "Invalid appspwn client");
AppSpawnClientExt *appProperty = (AppSpawnClientExt *)client;
appProperty->property.cloneFlags = client->cloneFlags;
int ret = SandboxUtils::SetAppSandboxProperty(&appProperty->property);
// for module test do not create sandbox
if (strncmp(appProperty->property.bundleName,
@ -59,3 +66,9 @@ int32_t SetAppSandboxProperty(struct AppSpawnContent_ *content, AppSpawnClient *
}
return ret;
}
int32_t GetAppNamespaceFlags(const char *bundleName)
{
return SandboxUtils::GetNamespaceFlagsFromConfig(bundleName);
}

View File

@ -19,6 +19,10 @@
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <sched.h>
#ifdef OHOS_DEBUG
#include <time.h>
#endif // OHOS_DEBUG
@ -31,6 +35,8 @@ void DisallowInternet(void);
#endif
#endif
#define SANDBOX_STACK_SIZE (1024 * 1024 * 8)
static void SetInternetPermission(const AppSpawnClient *client)
{
#ifndef APPSPAWN_TEST
@ -74,7 +80,7 @@ int DoStartApp(struct AppSpawnContent_ *content, AppSpawnClient *client, char *l
APPSPAWN_LOGI("DoStartApp id %d longProcNameLen %u", client->id, longProcNameLen);
int32_t ret = 0;
if (client->flags != UI_SERVICE_DIALOG && content->setAppSandbox) {
if ((client->cloneFlags & CLONE_NEWNS) && (content->setAppSandbox)) {
ret = content->setAppSandbox(content, client);
APPSPAWN_CHECK(ret == 0, NotifyResToParent(content, client, ret);
return ret, "Failed to set app sandbox");
@ -122,73 +128,96 @@ int DoStartApp(struct AppSpawnContent_ *content, AppSpawnClient *client, char *l
return 0;
}
int ForkChildProc(struct AppSpawnContent_ *content, AppSpawnClient *client, pid_t pid)
int AppSpawnChild(void *arg)
{
if (pid < 0) {
return -errno;
} else if (pid == 0) {
APPSPAWN_CHECK(arg != NULL, return -1, "Invalid arg for appspawn child");
AppSandboxArg *sandbox = (AppSandboxArg *)arg;
struct AppSpawnContent_ *content = sandbox->content;
AppSpawnClient *client = sandbox->client;
#ifdef OHOS_DEBUG
struct timespec tmStart = {0};
GetCurTime(&tmStart);
struct timespec tmStart = {0};
GetCurTime(&tmStart);
#endif // OHOS_DEBUG
// close socket id and signal for child
if (content->clearEnvironment != NULL) {
content->clearEnvironment(content, client);
}
// close socket id and signal for child
if (content->clearEnvironment != NULL) {
content->clearEnvironment(content, client);
}
if (content->setAppAccessToken != NULL) {
content->setAppAccessToken(content, client);
}
if (content->setAppAccessToken != NULL) {
content->setAppAccessToken(content, client);
}
int ret = -1;
int ret = -1;
#ifdef ASAN_DETECTOR
if ((content->getWrapBundleNameValue != NULL && content->getWrapBundleNameValue(content, client) == 0)
|| ((client->flags & APP_COLD_START) != 0)) {
if ((content->getWrapBundleNameValue != NULL && content->getWrapBundleNameValue(content, client) == 0)
|| ((client->flags & APP_COLD_START) != 0)) {
#else
if ((client->flags & APP_COLD_START) != 0) {
if ((client->flags & APP_COLD_START) != 0) {
#endif
if (content->coldStartApp != NULL && content->coldStartApp(content, client) == 0) {
if (content->coldStartApp != NULL && content->coldStartApp(content, client) == 0) {
#ifndef APPSPAWN_TEST
_exit(0x7f); // 0x7f user exit
_exit(0x7f); // 0x7f user exit
#endif
return -1;
} else {
ret = DoStartApp(content, client, content->longProcName, content->longProcNameLen);
}
return -1;
} else {
ret = DoStartApp(content, client, content->longProcName, content->longProcNameLen);
}
} else {
ret = DoStartApp(content, client, content->longProcName, content->longProcNameLen);
}
#ifdef OHOS_DEBUG
struct timespec tmEnd = {0};
GetCurTime(&tmEnd);
// 1s = 1000000000ns
long timeUsed = (tmEnd.tv_sec - tmStart.tv_sec) * 1000000000L + (tmEnd.tv_nsec - tmStart.tv_nsec);
APPSPAWN_LOGI("App timeused %d %ld ns.", getpid(), timeUsed);
struct timespec tmEnd = {0};
GetCurTime(&tmEnd);
// 1s = 1000000000ns
long timeUsed = (tmEnd.tv_sec - tmStart.tv_sec) * 1000000000L + (tmEnd.tv_nsec - tmStart.tv_nsec);
APPSPAWN_LOGI("App timeused %d %ld ns.", getpid(), timeUsed);
#endif // OHOS_DEBUG
if (ret == 0 && content->runChildProcessor != NULL) {
content->runChildProcessor(content, client);
}
ProcessExit();
if (ret == 0 && content->runChildProcessor != NULL) {
content->runChildProcessor(content, client);
}
ProcessExit();
return 0;
}
int AppSpawnProcessMsg(struct AppSpawnContent_ *content, AppSpawnClient *client, pid_t *childPid)
int AppSpawnProcessMsg(AppSandboxArg *sandbox, pid_t *childPid)
{
APPSPAWN_CHECK(content != NULL, return -1, "Invalid content for appspawn");
APPSPAWN_CHECK(client != NULL && childPid != NULL, return -1, "Invalid client for appspawn");
APPSPAWN_LOGI("AppSpawnProcessMsg id %d 0x%x", client->id, client->flags);
pid_t pid;
APPSPAWN_CHECK(sandbox != NULL && sandbox->content != NULL, return -1, "Invalid content for appspawn");
APPSPAWN_CHECK(sandbox->client != NULL && childPid != NULL, return -1, "Invalid client for appspawn");
APPSPAWN_LOGI("AppSpawnProcessMsg id %d 0x%x", sandbox->client->id, sandbox->client->flags);
#ifndef APPSPAWN_TEST
pid_t pid = fork();
#else
pid_t pid = 0;
#endif
int ret = ForkChildProc(content, client, pid);
APPSPAWN_CHECK(ret == 0, return ret, "fork child process error: %d", ret);
AppSpawnClient *client = sandbox->client;
if (client->cloneFlags & CLONE_NEWPID) {
APPSPAWN_CHECK(client->cloneFlags & CLONE_NEWNS, return -1, "clone flags error");
char *childStack = (char *)malloc(SANDBOX_STACK_SIZE);
APPSPAWN_CHECK(childStack != NULL, return -1, "malloc failed");
pid = clone(AppSpawnChild, childStack + SANDBOX_STACK_SIZE, client->cloneFlags | SIGCHLD, (void *)sandbox);
if (pid > 0) {
free(childStack);
*childPid = pid;
return 0;
}
client->cloneFlags &= ~CLONE_NEWPID;
free(childStack);
}
pid = fork();
APPSPAWN_CHECK(pid >= 0, return -errno, "fork child process error: %d", -errno);
*childPid = pid;
#else
pid = 0;
*childPid = pid;
#endif
if (pid == 0) {
AppSpawnChild((void *)sandbox);
}
return 0;
}

View File

@ -41,6 +41,7 @@ extern "C" {
typedef struct AppSpawnClient_ {
uint32_t id;
uint32_t flags;
uint32_t cloneFlags;
#ifndef APPSPAWN_TEST
#ifndef OHOS_LITE
uint8_t setAllowInternet;
@ -84,10 +85,15 @@ typedef struct AppSpawnContent_ {
int (*setSeccompFilter)(struct AppSpawnContent_ *content, AppSpawnClient *client);
} AppSpawnContent;
typedef struct {
struct AppSpawnContent_ *content;
AppSpawnClient *client;
} AppSandboxArg;
AppSpawnContent *AppSpawnCreateContent(const char *socketName, char *longProcName, uint32_t longProcNameLen, int cold);
int AppSpawnProcessMsg(struct AppSpawnContent_ *content, AppSpawnClient *client, pid_t *childPid);
int AppSpawnProcessMsg(AppSandboxArg *sandbox, pid_t *childPid);
int DoStartApp(struct AppSpawnContent_ *content, AppSpawnClient *client, char *longProcName, uint32_t longProcNameLen);
int ForkChildProc(struct AppSpawnContent_ *content, AppSpawnClient *client, pid_t pid);
int AppSpawnChild(void *arg);
#ifdef OHOS_DEBUG
void GetCurTime(struct timespec* tmCur);

View File

@ -32,9 +32,16 @@ ohos_prebuilt_etc("product-sandbox.json") {
module_install_dir = "etc/sandbox"
}
ohos_prebuilt_etc("sandbox-config.json") {
source = "//base/startup/appspawn/sandbox-config.json"
part_name = "${part_name}"
module_install_dir = "etc/sandbox"
}
group("etc_files") {
deps = [
":appdata-sandbox.json",
":product-sandbox.json",
":sandbox-config.json",
]
}

View File

@ -58,6 +58,7 @@ typedef enum AppOperateType_ {
#define FD_INIT_VALUE 0
typedef struct AppParameter_ {
uint32_t cloneFlags;
uint32_t uid; // the UNIX uid that the child process setuid() to after fork()
uint32_t gid; // the UNIX gid that the child process setgid() to after fork()
uint32_t gidTable[APP_MAX_GIDS]; // a list of UNIX gids that the child process setgroups() to after fork()

View File

@ -134,6 +134,7 @@ static int Invoke(IServerProxy *iProxy, int funcId, void *origin, IpcIo *req, Ip
AppSpawnClientLite client = {};
client.client.id = CLIENT_ID;
client.client.flags = 0;
client.client.cloneFlags = 0;
if (GetMessageSt(&client.message, req) != EC_SUCCESS) {
APPSPAWN_LOGE("[appspawn] invoke, parse failed! reply %d.", INVALID_PID);
WriteInt64(reply, INVALID_PID);
@ -142,14 +143,24 @@ static int Invoke(IServerProxy *iProxy, int funcId, void *origin, IpcIo *req, Ip
APPSPAWN_LOGI("[appspawn] invoke, msg<%s,%s,%d,%d %d>", client.message.bundleName, client.message.identityID,
client.message.uID, client.message.gID, client.message.capsCnt);
/* Clone support only one parameter, so need to package application parameters */
AppSandboxArg *sandboxArg = (AppSandboxArg *)malloc(sizeof(AppSandboxArg));
if (sandboxArg == NULL) {
WriteInt64(reply, INVALID_PID);
return EC_FAILURE;
}
(void)memset_s(sandboxArg, sizeof(AppSandboxArg), 0, sizeof(AppSandboxArg));
pid_t newPid = 0;
int ret = AppSpawnProcessMsg(&g_appSpawnContentLite->content, &client.client, &newPid);
sandboxArg->content = &g_appSpawnContentLite->content;
sandboxArg->client = &client.client;
int ret = AppSpawnProcessMsg(sandboxArg, &newPid);
if (ret != 0) {
newPid = -1;
}
FreeMessageSt(&client.message);
WriteInt64(reply, newPid);
free(sandboxArg);
#ifdef OHOS_DEBUG
struct timespec tmEnd = {0};

19
sandbox-config.json Executable file
View File

@ -0,0 +1,19 @@
{
"sandbox-namespace" : [{
"com.ohos.note" : [{
"clone-flags": [ "mnt", "pid" ]
}],
"com.ohos.screenlock" : [{
"clone-flags": [ "mnt", "pid" ]
}],
"ohos.samples.flashlight" : [{
"clone-flags": [ "mnt", "pid" ]
}],
"ohos.samples.ecg" : [{
"clone-flags": [ "mnt", "pid" ]
}],
"ohos.samples.clock" : [{
"clone-flags": [ "mnt", "pid" ]
}]
}]
}

View File

@ -28,6 +28,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sched.h>
#include "securec.h"
#include "parameter.h"
@ -165,18 +166,32 @@ static void ClearEnvironment(AppSpawnContent *content, AppSpawnClient *client)
static int SetUidGid(struct AppSpawnContent_ *content, AppSpawnClient *client)
{
#ifdef GRAPHIC_PERMISSION_CHECK
long ret;
AppSpawnClientExt *appProperty = (AppSpawnClientExt *)client;
// set gids
bool isRet = setgroups(appProperty->property.gidCount, (const gid_t *)(&appProperty->property.gidTable[0])) == -1;
APPSPAWN_CHECK(!isRet, return -errno, "setgroups failed: %d, gids.size=%u", errno, appProperty->property.gidCount);
// set gid
isRet = setresgid(appProperty->property.gid, appProperty->property.gid, appProperty->property.gid) == -1;
APPSPAWN_CHECK(!isRet, return -errno, "setgid(%u) failed: %d", appProperty->property.gid, errno);
if (client->cloneFlags & CLONE_NEWPID) {
/* setresuid and setresgid have multi-thread synchronous operations.
* after clone, the C library has not cleaned up the multi-thread information, so need to call syscall.
*/
// set gid
ret = syscall(SYS_setresgid, appProperty->property.gid, appProperty->property.gid, appProperty->property.gid);
APPSPAWN_CHECK(ret == 0, return -errno, "setgid(%u) failed: %d", appProperty->property.gid, errno);
// If the effective user ID is changed from 0 to nonzero, then all capabilities are cleared from the effective set
isRet = setresuid(appProperty->property.uid, appProperty->property.uid, appProperty->property.uid) == -1;
APPSPAWN_CHECK(!isRet, return -errno, "setuid(%u) failed: %d", appProperty->property.uid, errno);
// If the effective user ID is changed from 0 to nonzero, then all capabilities are cleared from the effective set
ret = syscall(SYS_setresuid, appProperty->property.uid, appProperty->property.uid, appProperty->property.uid);
APPSPAWN_CHECK(ret == 0, return -errno, "setuid(%u) failed: %d", appProperty->property.uid, errno);
} else {
// set gid
isRet = setresgid(appProperty->property.gid, appProperty->property.gid, appProperty->property.gid) == -1;
APPSPAWN_CHECK(!isRet, return -errno, "setgid(%u) failed: %d", appProperty->property.gid, errno);
// If the effective user ID is changed from 0 to nonzero, then all capabilities are cleared from the effective set
isRet = setresuid(appProperty->property.uid, appProperty->property.uid, appProperty->property.uid) == -1;
APPSPAWN_CHECK(!isRet, return -errno, "setuid(%u) failed: %d", appProperty->property.uid, errno);
}
#endif
return 0;
}

View File

@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sched.h>
#include "init_hashmap.h"
#include "init_socket.h"
@ -376,13 +377,30 @@ APPSPAWN_STATIC void OnReceiveRequest(const TaskHandle taskHandle, const uint8_t
appProperty->client.flags = appProperty->property.flags;
fcntl(appProperty->fd[0], F_SETFL, O_NONBLOCK);
/* Clone support only one parameter, so need to package application parameters */
AppSandboxArg *sandboxArg = (AppSandboxArg *)malloc(sizeof(AppSandboxArg));
if (sandboxArg == NULL) {
close(appProperty->fd[0]);
close(appProperty->fd[1]);
LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
return;
}
(void)memset_s(sandboxArg, sizeof(AppSandboxArg), 0, sizeof(AppSandboxArg));
pid_t pid = 0;
int result = AppSpawnProcessMsg(&g_appSpawnContent->content, &appProperty->client, &pid);
sandboxArg->content = &g_appSpawnContent->content;
sandboxArg->client = &appProperty->client;
sandboxArg->client->cloneFlags = 0;
if (appProperty->client.flags != UI_SERVICE_DIALOG) {
sandboxArg->client->cloneFlags = GetAppNamespaceFlags(appProperty->property.bundleName);
}
int result = AppSpawnProcessMsg(sandboxArg, &pid);
if (result == 0) { // wait child process result
result = WaitChild(appProperty->fd[0], pid, appProperty);
}
close(appProperty->fd[0]);
close(appProperty->fd[1]);
free(sandboxArg);
APPSPAWN_LOGI("child process %s %s pid %d",
appProperty->property.processName, (result == 0) ? "success" : "fail", pid);
if (result == 0) {

View File

@ -123,6 +123,7 @@ HWTEST(AppSpawnStandardTest, App_Spawn_Standard_002, TestSize.Level0)
AppSpawnClientExt* client = (AppSpawnClientExt*)malloc(sizeof(AppSpawnClientExt));
client->client.id = 8; // 8 is client id
client->client.flags = 0;
client->client.cloneFlags = CLONE_NEWNS;
client->fd[0] = 100; // 100 is fd
client->fd[1] = 200; // 200 is fd
client->property.uid = 10000; // 10000 is uid
@ -149,8 +150,11 @@ HWTEST(AppSpawnStandardTest, App_Spawn_Standard_002, TestSize.Level0)
content->loadExtendLib = LoadExtendLib;
content->runChildProcessor = RunChildProcessor;
SetContentFunction(content);
EXPECT_EQ(ForkChildProc(content, &client->client, 0), 0);
EXPECT_NE(ForkChildProc(content, &client->client, -1), 0);
AppSandboxArg *sandboxArg = (AppSandboxArg *)malloc(sizeof(AppSandboxArg));
sandboxArg->content = content;
sandboxArg->client = &client->client;
EXPECT_EQ(AppSpawnChild(sandboxArg), 0);
free(sandboxArg);
content->clearEnvironment(content, &client->client);
EXPECT_EQ(content->setProcessName(content, &client->client, (char *)longProcName, longProcNameLen), 0);

View File

@ -26,11 +26,14 @@ namespace OHOS {
namespace AppSpawn {
class SandboxUtils {
public:
static void StoreNamespaceJsonConfig(nlohmann::json &appNamespaceConfig);
static nlohmann::json GetNamespaceJsonConfig(void);
static void StoreJsonConfig(nlohmann::json &appSandboxConfig);
static nlohmann::json GetJsonConfig();
static void StoreProductJsonConfig(nlohmann::json &productSandboxConfig);
static nlohmann::json GetProductJsonConfig();
static int32_t SetAppSandboxProperty(const ClientSocket::AppProperty *appProperty);
static int32_t GetNamespaceFlagsFromConfig(const char *bundleName);
private:
static int32_t DoAppSandboxMountOnce(const char *originPath, const char *destinationPath,
@ -69,6 +72,7 @@ private:
nlohmann::json &config);
private:
static nlohmann::json appNamespaceConfig_;
static nlohmann::json appSandboxConfig_;
static nlohmann::json productSandboxConfig_;
};

View File

@ -71,11 +71,24 @@ namespace {
const char *TARGET_NAME = "target-name";
const char *FLAGS_POINT = "flags-point";
const char *FLAGS = "flags";
const char *SANDBOX_NAMESPACE = "sandbox-namespace";
const char *SANDBOX_CLONE_FLAGS = "clone-flags";
}
nlohmann::json SandboxUtils::appNamespaceConfig_;
nlohmann::json SandboxUtils::appSandboxConfig_;
nlohmann::json SandboxUtils::productSandboxConfig_;
void SandboxUtils::StoreNamespaceJsonConfig(nlohmann::json &appNamespaceConfig)
{
SandboxUtils::appNamespaceConfig_ = appNamespaceConfig;
}
nlohmann::json SandboxUtils::GetNamespaceJsonConfig(void)
{
return SandboxUtils::appNamespaceConfig_;
}
void SandboxUtils::StoreJsonConfig(nlohmann::json &appSandboxConfig)
{
SandboxUtils::appSandboxConfig_ = appSandboxConfig;
@ -96,6 +109,39 @@ nlohmann::json SandboxUtils::GetProductJsonConfig()
return SandboxUtils::productSandboxConfig_;
}
static int32_t NamespaceFlagsFromConfig(const std::vector<std::string> &vec)
{
const std::map<std::string, int32_t> NamespaceFlagsMap = { {"mnt", CLONE_NEWNS}, {"pid", CLONE_NEWPID} };
int32_t cloneFlags = 0;
for (unsigned int j = 0; j < vec.size(); j++) {
if (NamespaceFlagsMap.count(vec[j])) {
cloneFlags |= NamespaceFlagsMap.at(vec[j]);
}
}
return cloneFlags;
}
int32_t SandboxUtils::GetNamespaceFlagsFromConfig(const char *bundleName)
{
nlohmann::json config = SandboxUtils::GetNamespaceJsonConfig();
int32_t cloneFlags = CLONE_NEWNS;
if (config.find(SANDBOX_NAMESPACE) == config.end()) {
APPSPAWN_LOGE("namespace config is not found");
return 0;
}
nlohmann::json namespaceApp = config[SANDBOX_NAMESPACE][0];
if (namespaceApp.find(bundleName) == namespaceApp.end()) {
return cloneFlags;
}
nlohmann::json app = namespaceApp[bundleName][0];
cloneFlags |= NamespaceFlagsFromConfig(app[SANDBOX_CLONE_FLAGS].get<std::vector<std::string>>());
return cloneFlags;
}
static void MakeDirRecursive(const std::string &path, mode_t mode)
{
size_t size = path.size();
@ -723,9 +769,13 @@ int32_t SandboxUtils::SetAppSandboxProperty(const ClientSocket::AppProperty *app
sandboxPackagePath += bundleName;
mkdir(sandboxPackagePath.c_str(), FILE_MODE);
// add pid to a new mnt namespace
int rc = unshare(CLONE_NEWNS);
APPSPAWN_CHECK(rc == 0, return rc, "unshare failed, packagename is %s", bundleName.c_str());
int rc;
// when CLONE_NEWPID is enabled, CLONE_NEWNS must be enabled.
if (!(appProperty->cloneFlags & CLONE_NEWPID)) {
// add pid to a new mnt namespace
rc = unshare(CLONE_NEWNS);
APPSPAWN_CHECK(rc == 0, return rc, "unshare failed, packagename is %s", bundleName.c_str());
}
// to make wargnar work and check app sandbox switch
if ((CheckTotalSandboxSwitchStatus(appProperty) == false) ||