169570cc8Sopenharmony_ci/*
269570cc8Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
369570cc8Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
469570cc8Sopenharmony_ci * you may not use this file except in compliance with the License.
569570cc8Sopenharmony_ci * You may obtain a copy of the License at
669570cc8Sopenharmony_ci *
769570cc8Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
869570cc8Sopenharmony_ci *
969570cc8Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1069570cc8Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1169570cc8Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1269570cc8Sopenharmony_ci * See the License for the specific language governing permissions and
1369570cc8Sopenharmony_ci * limitations under the License.
1469570cc8Sopenharmony_ci */
1569570cc8Sopenharmony_ci
1669570cc8Sopenharmony_ci#include <dirent.h>
1769570cc8Sopenharmony_ci#include <fcntl.h>
1869570cc8Sopenharmony_ci#include <sched.h>
1969570cc8Sopenharmony_ci#include <signal.h>
2069570cc8Sopenharmony_ci#include <stdio.h>
2169570cc8Sopenharmony_ci#include <stdlib.h>
2269570cc8Sopenharmony_ci#include <unistd.h>
2369570cc8Sopenharmony_ci
2469570cc8Sopenharmony_ci#include "appspawn_hook.h"
2569570cc8Sopenharmony_ci#include "appspawn_manager.h"
2669570cc8Sopenharmony_ci#include "appspawn_utils.h"
2769570cc8Sopenharmony_ci#include "securec.h"
2869570cc8Sopenharmony_ci#ifdef WITH_SELINUX
2969570cc8Sopenharmony_ci#include "selinux/selinux.h"
3069570cc8Sopenharmony_ci#endif
3169570cc8Sopenharmony_ci
3269570cc8Sopenharmony_ci#define PID_NS_INIT_UID 100000  // reserved for pid_ns_init process, avoid app, render proc, etc.
3369570cc8Sopenharmony_ci#define PID_NS_INIT_GID 100000
3469570cc8Sopenharmony_ci
3569570cc8Sopenharmony_citypedef struct TagAppSpawnNamespace {
3669570cc8Sopenharmony_ci    AppSpawnExtData extData;
3769570cc8Sopenharmony_ci    int nsSelfPidFd;  // ns pid fd of appspawn
3869570cc8Sopenharmony_ci    int nsInitPidFd;  // ns pid fd of pid_ns_init
3969570cc8Sopenharmony_ci} AppSpawnNamespace;
4069570cc8Sopenharmony_ci
4169570cc8Sopenharmony_ciAPPSPAWN_STATIC pid_t GetPidByName(const char *name);
4269570cc8Sopenharmony_cistatic int AppSpawnExtDataCompareDataId(ListNode *node, void *data)
4369570cc8Sopenharmony_ci{
4469570cc8Sopenharmony_ci    AppSpawnExtData *extData = (AppSpawnExtData *)ListEntry(node, AppSpawnExtData, node);
4569570cc8Sopenharmony_ci    return extData->dataId - *(uint32_t *)data;
4669570cc8Sopenharmony_ci}
4769570cc8Sopenharmony_ci
4869570cc8Sopenharmony_ciAPPSPAWN_STATIC AppSpawnNamespace *GetAppSpawnNamespace(const AppSpawnMgr *content)
4969570cc8Sopenharmony_ci{
5069570cc8Sopenharmony_ci    APPSPAWN_CHECK_ONLY_EXPER(content != NULL, return NULL);
5169570cc8Sopenharmony_ci    uint32_t dataId = EXT_DATA_NAMESPACE;
5269570cc8Sopenharmony_ci    ListNode *node = OH_ListFind(&content->extData, (void *)&dataId, AppSpawnExtDataCompareDataId);
5369570cc8Sopenharmony_ci    if (node == NULL) {
5469570cc8Sopenharmony_ci        return NULL;
5569570cc8Sopenharmony_ci    }
5669570cc8Sopenharmony_ci    return (AppSpawnNamespace *)ListEntry(node, AppSpawnNamespace, extData);
5769570cc8Sopenharmony_ci}
5869570cc8Sopenharmony_ci
5969570cc8Sopenharmony_ciAPPSPAWN_STATIC void DeleteAppSpawnNamespace(AppSpawnNamespace *namespace)
6069570cc8Sopenharmony_ci{
6169570cc8Sopenharmony_ci    APPSPAWN_CHECK_ONLY_EXPER(namespace != NULL, return);
6269570cc8Sopenharmony_ci    APPSPAWN_LOGV("DeleteAppSpawnNamespace");
6369570cc8Sopenharmony_ci    OH_ListRemove(&namespace->extData.node);
6469570cc8Sopenharmony_ci    OH_ListInit(&namespace->extData.node);
6569570cc8Sopenharmony_ci
6669570cc8Sopenharmony_ci    if (namespace->nsInitPidFd > 0) {
6769570cc8Sopenharmony_ci        close(namespace->nsInitPidFd);
6869570cc8Sopenharmony_ci        namespace->nsInitPidFd = -1;
6969570cc8Sopenharmony_ci    }
7069570cc8Sopenharmony_ci    if (namespace->nsSelfPidFd > 0) {
7169570cc8Sopenharmony_ci        close(namespace->nsSelfPidFd);
7269570cc8Sopenharmony_ci        namespace->nsSelfPidFd = -1;
7369570cc8Sopenharmony_ci    }
7469570cc8Sopenharmony_ci    free(namespace);
7569570cc8Sopenharmony_ci}
7669570cc8Sopenharmony_ci
7769570cc8Sopenharmony_ciAPPSPAWN_STATIC void FreeAppSpawnNamespace(struct TagAppSpawnExtData *data)
7869570cc8Sopenharmony_ci{
7969570cc8Sopenharmony_ci    AppSpawnNamespace *namespace = ListEntry(data, AppSpawnNamespace, extData);
8069570cc8Sopenharmony_ci    APPSPAWN_CHECK_ONLY_EXPER(namespace != NULL, return);
8169570cc8Sopenharmony_ci    DeleteAppSpawnNamespace(namespace);
8269570cc8Sopenharmony_ci}
8369570cc8Sopenharmony_ci
8469570cc8Sopenharmony_ciAPPSPAWN_STATIC AppSpawnNamespace *CreateAppSpawnNamespace(void)
8569570cc8Sopenharmony_ci{
8669570cc8Sopenharmony_ci    APPSPAWN_LOGV("CreateAppSpawnNamespace");
8769570cc8Sopenharmony_ci    AppSpawnNamespace *namespace = (AppSpawnNamespace *)calloc(1, sizeof(AppSpawnNamespace));
8869570cc8Sopenharmony_ci    APPSPAWN_CHECK(namespace != NULL, return NULL, "Failed to create sandbox");
8969570cc8Sopenharmony_ci    namespace->nsInitPidFd = -1;
9069570cc8Sopenharmony_ci    namespace->nsSelfPidFd = -1;
9169570cc8Sopenharmony_ci    // ext data init
9269570cc8Sopenharmony_ci    OH_ListInit(&namespace->extData.node);
9369570cc8Sopenharmony_ci    namespace->extData.dataId = EXT_DATA_NAMESPACE;
9469570cc8Sopenharmony_ci    namespace->extData.freeNode = FreeAppSpawnNamespace;
9569570cc8Sopenharmony_ci    namespace->extData.dumpNode = NULL;
9669570cc8Sopenharmony_ci    return namespace;
9769570cc8Sopenharmony_ci}
9869570cc8Sopenharmony_ci
9969570cc8Sopenharmony_ciAPPSPAWN_STATIC pid_t GetPidByName(const char *name)
10069570cc8Sopenharmony_ci{
10169570cc8Sopenharmony_ci    int pid = -1;  // initial pid set to -1
10269570cc8Sopenharmony_ci    DIR *dir = opendir("/proc");
10369570cc8Sopenharmony_ci    if (dir == NULL) {
10469570cc8Sopenharmony_ci        return -1;
10569570cc8Sopenharmony_ci    }
10669570cc8Sopenharmony_ci
10769570cc8Sopenharmony_ci    struct dirent *entry;
10869570cc8Sopenharmony_ci    while ((entry = readdir(dir)) != NULL) {
10969570cc8Sopenharmony_ci        if (entry->d_type != DT_DIR) {
11069570cc8Sopenharmony_ci            continue;
11169570cc8Sopenharmony_ci        }
11269570cc8Sopenharmony_ci        long pidNum = strtol(entry->d_name, NULL, 10);  // pid will not exceed a 10-digit decimal number
11369570cc8Sopenharmony_ci        if (pidNum <= 0) {
11469570cc8Sopenharmony_ci            continue;
11569570cc8Sopenharmony_ci        }
11669570cc8Sopenharmony_ci
11769570cc8Sopenharmony_ci        char path[32];  // path that contains the process name
11869570cc8Sopenharmony_ci        if (snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%s/comm", entry->d_name) < 0) {
11969570cc8Sopenharmony_ci            continue;
12069570cc8Sopenharmony_ci        }
12169570cc8Sopenharmony_ci        FILE *file = fopen(path, "r");
12269570cc8Sopenharmony_ci        if (file == NULL) {
12369570cc8Sopenharmony_ci            continue;
12469570cc8Sopenharmony_ci        }
12569570cc8Sopenharmony_ci        char buffer[32];  // read the process name
12669570cc8Sopenharmony_ci        if (fgets(buffer, sizeof(buffer), file) == NULL) {
12769570cc8Sopenharmony_ci            (void)fclose(file);
12869570cc8Sopenharmony_ci            continue;
12969570cc8Sopenharmony_ci        }
13069570cc8Sopenharmony_ci        buffer[strcspn(buffer, "\n")] = 0;
13169570cc8Sopenharmony_ci        if (strcmp(buffer, name) != 0) {
13269570cc8Sopenharmony_ci            (void)fclose(file);
13369570cc8Sopenharmony_ci            continue;
13469570cc8Sopenharmony_ci        }
13569570cc8Sopenharmony_ci
13669570cc8Sopenharmony_ci        APPSPAWN_LOGI("get pid of %{public}s success", name);
13769570cc8Sopenharmony_ci        pid = (int)pidNum;
13869570cc8Sopenharmony_ci        (void)fclose(file);
13969570cc8Sopenharmony_ci        break;
14069570cc8Sopenharmony_ci    }
14169570cc8Sopenharmony_ci
14269570cc8Sopenharmony_ci    closedir(dir);
14369570cc8Sopenharmony_ci    return pid;
14469570cc8Sopenharmony_ci}
14569570cc8Sopenharmony_ci
14669570cc8Sopenharmony_ciAPPSPAWN_STATIC int NsInitFunc()
14769570cc8Sopenharmony_ci{
14869570cc8Sopenharmony_ci    setuid(PID_NS_INIT_UID);
14969570cc8Sopenharmony_ci    setgid(PID_NS_INIT_GID);
15069570cc8Sopenharmony_ci#ifdef WITH_SELINUX
15169570cc8Sopenharmony_ci    setcon("u:r:pid_ns_init:s0");
15269570cc8Sopenharmony_ci#endif
15369570cc8Sopenharmony_ci    char *argv[] = {"/system/bin/pid_ns_init", NULL};
15469570cc8Sopenharmony_ci    execve("/system/bin/pid_ns_init", argv, NULL);
15569570cc8Sopenharmony_ci#ifndef APPSPAWN_TEST
15669570cc8Sopenharmony_ci    _exit(0);
15769570cc8Sopenharmony_ci#endif
15869570cc8Sopenharmony_ci    return 0;
15969570cc8Sopenharmony_ci}
16069570cc8Sopenharmony_ci
16169570cc8Sopenharmony_ciAPPSPAWN_STATIC int GetNsPidFd(pid_t pid)
16269570cc8Sopenharmony_ci{
16369570cc8Sopenharmony_ci    char nsPath[256];  // filepath of ns pid
16469570cc8Sopenharmony_ci    int ret = snprintf_s(nsPath, sizeof(nsPath), sizeof(nsPath) - 1, "/proc/%d/ns/pid", pid);
16569570cc8Sopenharmony_ci    if (ret < 0) {
16669570cc8Sopenharmony_ci        APPSPAWN_LOGE("SetPidNamespace failed, snprintf_s error:%{public}s", strerror(errno));
16769570cc8Sopenharmony_ci        return -1;
16869570cc8Sopenharmony_ci    }
16969570cc8Sopenharmony_ci    int nsFd = open(nsPath, O_RDONLY);
17069570cc8Sopenharmony_ci    if (nsFd < 0) {
17169570cc8Sopenharmony_ci        APPSPAWN_LOGE("open ns pid:%{public}d failed, err:%{public}s", pid, strerror(errno));
17269570cc8Sopenharmony_ci        return -1;
17369570cc8Sopenharmony_ci    }
17469570cc8Sopenharmony_ci    return nsFd;
17569570cc8Sopenharmony_ci}
17669570cc8Sopenharmony_ci
17769570cc8Sopenharmony_ciAPPSPAWN_STATIC int PreLoadEnablePidNs(AppSpawnMgr *content)
17869570cc8Sopenharmony_ci{
17969570cc8Sopenharmony_ci    APPSPAWN_LOGI("Enable pid namespace flags: 0x%{public}x", content->content.sandboxNsFlags);
18069570cc8Sopenharmony_ci    if (IsColdRunMode(content)) {
18169570cc8Sopenharmony_ci        return 0;
18269570cc8Sopenharmony_ci    }
18369570cc8Sopenharmony_ci    if (IsNWebSpawnMode(content)) {  // only for appspawn
18469570cc8Sopenharmony_ci        return 0;
18569570cc8Sopenharmony_ci    }
18669570cc8Sopenharmony_ci    if (!(content->content.sandboxNsFlags & CLONE_NEWPID)) {
18769570cc8Sopenharmony_ci        return 0;
18869570cc8Sopenharmony_ci    }
18969570cc8Sopenharmony_ci    AppSpawnNamespace *namespace = CreateAppSpawnNamespace();
19069570cc8Sopenharmony_ci    APPSPAWN_CHECK(namespace != NULL, return -1, "Failed to create namespace");
19169570cc8Sopenharmony_ci
19269570cc8Sopenharmony_ci    int ret = -1;
19369570cc8Sopenharmony_ci    // check if process pid_ns_init exists, this is the init process for pid namespace
19469570cc8Sopenharmony_ci    pid_t pid = GetPidByName("pid_ns_init");
19569570cc8Sopenharmony_ci    if (pid == -1) {
19669570cc8Sopenharmony_ci        APPSPAWN_LOGI("Start Create pid_ns_init %{public}d", pid);
19769570cc8Sopenharmony_ci        pid = clone(NsInitFunc, NULL, CLONE_NEWPID, NULL);
19869570cc8Sopenharmony_ci        if (pid < 0) {
19969570cc8Sopenharmony_ci            APPSPAWN_LOGE("clone pid ns init failed");
20069570cc8Sopenharmony_ci            DeleteAppSpawnNamespace(namespace);
20169570cc8Sopenharmony_ci            return ret;
20269570cc8Sopenharmony_ci        }
20369570cc8Sopenharmony_ci    } else {
20469570cc8Sopenharmony_ci        APPSPAWN_LOGI("pid_ns_init exists, no need to create");
20569570cc8Sopenharmony_ci    }
20669570cc8Sopenharmony_ci
20769570cc8Sopenharmony_ci    namespace->nsSelfPidFd = GetNsPidFd(getpid());
20869570cc8Sopenharmony_ci    if (namespace->nsSelfPidFd < 0) {
20969570cc8Sopenharmony_ci        APPSPAWN_LOGE("open ns pid of appspawn fail");
21069570cc8Sopenharmony_ci        DeleteAppSpawnNamespace(namespace);
21169570cc8Sopenharmony_ci        return ret;
21269570cc8Sopenharmony_ci    }
21369570cc8Sopenharmony_ci
21469570cc8Sopenharmony_ci    namespace->nsInitPidFd = GetNsPidFd(pid);
21569570cc8Sopenharmony_ci    if (namespace->nsInitPidFd < 0) {
21669570cc8Sopenharmony_ci        APPSPAWN_LOGE("open ns pid of pid_ns_init fail");
21769570cc8Sopenharmony_ci        DeleteAppSpawnNamespace(namespace);
21869570cc8Sopenharmony_ci        return ret;
21969570cc8Sopenharmony_ci    }
22069570cc8Sopenharmony_ci    OH_ListAddTail(&content->extData, &namespace->extData.node);
22169570cc8Sopenharmony_ci    APPSPAWN_LOGI("Enable pid namespace success.");
22269570cc8Sopenharmony_ci    return 0;
22369570cc8Sopenharmony_ci}
22469570cc8Sopenharmony_ci
22569570cc8Sopenharmony_ci// after calling setns, new process will be in the same pid namespace of the input pid
22669570cc8Sopenharmony_cistatic int SetPidNamespace(int nsPidFd, int nsType)
22769570cc8Sopenharmony_ci{
22869570cc8Sopenharmony_ci    APPSPAWN_LOGI("SetPidNamespace 0x%{public}x", nsType);
22969570cc8Sopenharmony_ci#ifndef APPSPAWN_TEST
23069570cc8Sopenharmony_ci    if (setns(nsPidFd, nsType) < 0) {
23169570cc8Sopenharmony_ci        APPSPAWN_LOGE("set pid namespace nsType:%{public}d failed", nsType);
23269570cc8Sopenharmony_ci        return -1;
23369570cc8Sopenharmony_ci    }
23469570cc8Sopenharmony_ci#endif
23569570cc8Sopenharmony_ci    return 0;
23669570cc8Sopenharmony_ci}
23769570cc8Sopenharmony_ci
23869570cc8Sopenharmony_ciAPPSPAWN_STATIC int PreForkSetPidNamespace(AppSpawnMgr *content, AppSpawningCtx *property)
23969570cc8Sopenharmony_ci{
24069570cc8Sopenharmony_ci    AppSpawnNamespace *namespace = GetAppSpawnNamespace(content);
24169570cc8Sopenharmony_ci    if (namespace == NULL) {
24269570cc8Sopenharmony_ci        return 0;
24369570cc8Sopenharmony_ci    }
24469570cc8Sopenharmony_ci    if (content->content.sandboxNsFlags & CLONE_NEWPID) {
24569570cc8Sopenharmony_ci        SetPidNamespace(namespace->nsInitPidFd, CLONE_NEWPID);  // pid_ns_init is the init process
24669570cc8Sopenharmony_ci    }
24769570cc8Sopenharmony_ci    return 0;
24869570cc8Sopenharmony_ci}
24969570cc8Sopenharmony_ci
25069570cc8Sopenharmony_ciAPPSPAWN_STATIC int PostForkSetPidNamespace(AppSpawnMgr *content, AppSpawningCtx *property)
25169570cc8Sopenharmony_ci{
25269570cc8Sopenharmony_ci    AppSpawnNamespace *namespace = GetAppSpawnNamespace(content);
25369570cc8Sopenharmony_ci    if (namespace == NULL) {
25469570cc8Sopenharmony_ci        return 0;
25569570cc8Sopenharmony_ci    }
25669570cc8Sopenharmony_ci    if (content->content.sandboxNsFlags & CLONE_NEWPID) {
25769570cc8Sopenharmony_ci        SetPidNamespace(namespace->nsSelfPidFd, 0);  // go back to original pid namespace
25869570cc8Sopenharmony_ci    }
25969570cc8Sopenharmony_ci
26069570cc8Sopenharmony_ci    return 0;
26169570cc8Sopenharmony_ci}
26269570cc8Sopenharmony_ci
26369570cc8Sopenharmony_ciMODULE_CONSTRUCTOR(void)
26469570cc8Sopenharmony_ci{
26569570cc8Sopenharmony_ci    AddPreloadHook(HOOK_PRIO_LOWEST, PreLoadEnablePidNs);
26669570cc8Sopenharmony_ci    AddAppSpawnHook(STAGE_PARENT_PRE_FORK, HOOK_PRIO_LOWEST, PreForkSetPidNamespace);
26769570cc8Sopenharmony_ci    AddAppSpawnHook(STAGE_PARENT_POST_FORK, HOOK_PRIO_HIGHEST, PostForkSetPidNamespace);
26869570cc8Sopenharmony_ci}
269