5b9259e8创建于 6 天前历史提交
/*
 * Copyright (C) 2025 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 "sandbox_dec.h"

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "appspawn_utils.h"
#include "appspawn_hook.h"
#include "appspawn_manager.h"
#include "dec_config.h"
#include "securec.h"

static const char *g_decConstraintDir[] = {
    "/storage/Users",
    "/storage/External",
    "/storage/Share",
    "/storage/hmdfs",
    "/mnt/data/fuse",
    "/mnt/debug",
    "/storage/userExternal",
    "/storage/ANCO_APP_DATA"
};

static const char *g_decForcedPrefix[] = {
    "/storage/Users/currentUser/appdata",
};

APPSPAWN_STATIC int SetIgnoreCaseDirs(AppSpawnMgr *content)
{
    APPSPAWN_CHECK(IsNativeSpawnMode(content) || IsAppSpawnMode(content), return 0,
        "not support ignore case dir");

    uint32_t pathNum = 0;
    const DecIgnoreCaseInfo *setInfo = GetDecIgnoreCaseList(IsNoShareFsEnable(), &pathNum);
    APPSPAWN_CHECK(setInfo != NULL && pathNum > 0, return 0, "invalid dec ignore case list");
    const char *decFilename = "/dev/dec";
    int fd = open(decFilename, O_RDWR);
    APPSPAWN_CHECK(fd >= 0, return 0, "Open dec file failed errno %{public}d", errno);

    DecPolicyInfo decPolicyInfos = {0};
    decPolicyInfos.tokenId = 0;
    decPolicyInfos.pathNum = pathNum;
    decPolicyInfos.flag = 0;

    for (uint32_t i = 0; i < pathNum; i++) {
        PathInfo pathInfo = {
            .path = (char *)setInfo[i].path,
            .pathLen = (uint32_t)strlen(setInfo[i].path),
            .mode = (uint32_t)setInfo[i].mode,
            .flag = false
        };
        APPSPAWN_LOGV("set ignore case dec policy %{public}s %{public}d", setInfo[i].path, setInfo[i].mode);
        decPolicyInfos.path[i] = pathInfo;
    }
    int ret = ioctl(fd, SET_DEC_IGNORE_CASE_CMD, &decPolicyInfos);
    APPSPAWN_CHECK_ONLY_LOG(ret >= 0, "set dec ignore failed errno %{public}d", errno);
    close(fd);
    return 0;
}

static GlobalDecPolicyInfo *g_decPolicyInfos = NULL;

void DestroyDecPolicyInfos(GlobalDecPolicyInfo *decPolicyInfos)
{
    if (decPolicyInfos == NULL) {
        return;
    }
    for (uint32_t i = 0; i < decPolicyInfos->pathNum; i++) {
        if (decPolicyInfos->path[i].path) {
            free(decPolicyInfos->path[i].path);
            decPolicyInfos->path[i].pathLen = 0;
            decPolicyInfos->path[i].flag = 0;
            decPolicyInfos->path[i].mode = 0;
        }
    }
    decPolicyInfos->pathNum = 0;
    decPolicyInfos->tokenId = 0;
    decPolicyInfos->flag = 0;
    free(decPolicyInfos);
}

void SetDecPolicyInfos(DecPolicyInfo *decPolicyInfos)
{
    if (decPolicyInfos == NULL || decPolicyInfos->pathNum == 0) {
        return;
    }

    if (g_decPolicyInfos == NULL) {
        g_decPolicyInfos = (GlobalDecPolicyInfo *)calloc(1, sizeof(GlobalDecPolicyInfo));
        if (g_decPolicyInfos == NULL) {
            APPSPAWN_LOGE("calloc failed");
            return;
        }
    }

    APPSPAWN_CHECK(g_decPolicyInfos->pathNum + decPolicyInfos->pathNum <= MAX_POLICY_NUM,
        DestroyDecPolicyInfos(g_decPolicyInfos);
        g_decPolicyInfos = NULL;
        return, "Out of MAX_POLICY_NUM %{public}d, %{public}d", g_decPolicyInfos->pathNum, decPolicyInfos->pathNum);
    for (uint32_t i = 0; i < decPolicyInfos->pathNum; i++) {
        PathInfo pathInfo = {0};
        if (decPolicyInfos->path[i].path == NULL) {
            DestroyDecPolicyInfos(g_decPolicyInfos);
            g_decPolicyInfos = NULL;
            return;
        }
        pathInfo.path = strdup(decPolicyInfos->path[i].path);
        if (pathInfo.path == NULL) {
            DestroyDecPolicyInfos(g_decPolicyInfos);
            g_decPolicyInfos = NULL;
            return;
        }
        pathInfo.pathLen = (uint32_t)strlen(pathInfo.path);
        pathInfo.mode = decPolicyInfos->path[i].mode;
        uint32_t index = g_decPolicyInfos->pathNum + i;
        g_decPolicyInfos->path[index] = pathInfo;
    }
    g_decPolicyInfos->tokenId = decPolicyInfos->tokenId;
    g_decPolicyInfos->pathNum += decPolicyInfos->pathNum;
    g_decPolicyInfos->flag = false;
    g_decPolicyInfos->userId = 0;
}

static int SetDenyConstraintDirs(AppSpawnMgr *content)
{
    UNUSED(content);
    const char *decFilename = "/dev/dec";
    int fd = open(decFilename, O_RDWR);
    APPSPAWN_CHECK(fd >= 0, return 0, "Open dec file failed errno %{public}d", errno);

    uint32_t decDirsSize = ARRAY_LENGTH(g_decConstraintDir);
    DecPolicyInfo decPolicyInfos = {0};
    decPolicyInfos.tokenId = 0;
    decPolicyInfos.pathNum = decDirsSize;
    decPolicyInfos.flag = 0;

    for (uint32_t i = 0; i < decDirsSize; i++) {
        PathInfo pathInfo = {(char *)g_decConstraintDir[i], (uint32_t)strlen(g_decConstraintDir[i]), SANDBOX_MODE_READ};
        decPolicyInfos.path[i] = pathInfo;
    }

    if (ioctl(fd, CONSTRAINT_DEC_POLICY_CMD, &decPolicyInfos) < 0) {
        APPSPAWN_LOGE("set deny constraint sandbox policy failed errno %{public}d", errno);
    } else {
        APPSPAWN_LOGI("set CONSTRAINT_DEC_POLICY_CMD sandbox policy success");
        for (uint32_t i = 0; i < decPolicyInfos.pathNum; i++) {
            APPSPAWN_DUMPI("%{public}s", decPolicyInfos.path[i].path);
        }
    }
    close(fd);
    return 0;
}

static int SetForcedPrefixDirs(AppSpawnMgr *content)
{
    UNUSED(content);
    const char *decFilename = "/dev/dec";
    int fd = open(decFilename, O_RDWR);
    APPSPAWN_CHECK(fd >= 0, return 0, "Open dec file failed errno %{public}d", errno);

    uint32_t decDirsSize = ARRAY_LENGTH(g_decForcedPrefix);
    DecPolicyInfo decPolicyInfos = {0};
    decPolicyInfos.tokenId = 0;
    decPolicyInfos.pathNum = decDirsSize;
    decPolicyInfos.flag = 0;

    for (uint32_t i = 0; i < decDirsSize; i++) {
        PathInfo pathInfo = {(char *)g_decForcedPrefix[i], (uint32_t)strlen(g_decForcedPrefix[i]), SANDBOX_MODE_READ};
        decPolicyInfos.path[i] = pathInfo;
    }

    if (ioctl(fd, SET_DEC_PREFIX_CMD, &decPolicyInfos) < 0) {
        APPSPAWN_LOGE("set forced prefix sandbox policy failed errno %{public}d", errno);
    } else {
        APPSPAWN_LOGI("set SET_DEC_PREFIX_CMD sandbox policy success");
        for (uint32_t i = 0; i < decPolicyInfos.pathNum; i++) {
            APPSPAWN_DUMPI("%{public}s", decPolicyInfos.path[i].path);
        }
    }
    close(fd);
    return 0;
}

/**
 * @brief 下发单批次DEC策略到内核
 * @param fd dec设备文件描述符
 * @param decPolicyInfos 完整的策略信息
 * @param timestamp 时间戳
 * @param start 起始路径索引
 * @param count 本批次路径数量
 * @return 0成功,负数失败
 */
APPSPAWN_STATIC int SetDecPolicyBatch(int fd, GlobalDecPolicyInfo *decPolicyInfos,
    uint64_t timestamp, uint32_t start, uint32_t count)
{
    APPSPAWN_CHECK(decPolicyInfos != NULL && count > 0 && count <= KERNEL_BATCH_SIZE,
        return -1, "Invalid param");

    DecPolicyInfo batchInfo = {0};
    batchInfo.tokenId = decPolicyInfos->tokenId;
    batchInfo.timestamp = timestamp;
    batchInfo.pathNum = count;
    batchInfo.userId = decPolicyInfos->userId;
    batchInfo.flag = decPolicyInfos->flag;
    errno_t memRet = memcpy_s(batchInfo.reserved, sizeof(batchInfo.reserved),
                              decPolicyInfos->reserved, sizeof(decPolicyInfos->reserved));
    APPSPAWN_CHECK(memRet == EOK, return -1, "Failed to memcpy_s reserved");

    for (uint32_t i = 0; i < count; i++) {
        APPSPAWN_LOGV("SetDecPolicyBatch: [%{public}u] %{public}u %{public}u %{public}u %{public}s",
                      start + i, decPolicyInfos->path[start + i].mode, decPolicyInfos->path[start + i].flag,
                      decPolicyInfos->path[start + i].pathLen, decPolicyInfos->path[start + i].path);
        batchInfo.path[i].path = decPolicyInfos->path[start + i].path;
        batchInfo.path[i].pathLen = decPolicyInfos->path[start + i].pathLen;
        batchInfo.path[i].mode = decPolicyInfos->path[start + i].mode;
        batchInfo.path[i].flag = decPolicyInfos->path[start + i].flag;
    }

    int ret = ioctl(fd, SET_DEC_POLICY_CMD, &batchInfo);
    APPSPAWN_CHECK(ret >= 0, return ret,
        "SetDecPolicyBatch: ioctl failed, start %{public}u, count %{public}u, errno %{public}d",
        start, count, errno);
    return ret;
}

void SetDecPolicy(void)
{
    APPSPAWN_CHECK(g_decPolicyInfos != NULL, return, "Invalid g_decPolicyInfos");
    const char *decFilename = "/dev/dec";
    int fd = open(decFilename, O_RDWR);
    if (fd < 0) {
        APPSPAWN_LOGE("Open dec file failed errno %{public}d", errno);
        DestroyDecPolicyInfos(g_decPolicyInfos);
        g_decPolicyInfos = NULL;
        return;
    }

    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    uint64_t timestamp = ts.tv_sec * APPSPAWN_SEC_TO_NSEC + ts.tv_nsec;

    uint32_t pathNum = g_decPolicyInfos->pathNum;
    uint32_t batches = (pathNum + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
    for (uint32_t batch = 0; batch < batches; batch++) {
        uint32_t start = batch * KERNEL_BATCH_SIZE;
        uint32_t end = start + KERNEL_BATCH_SIZE;
        if (end > pathNum) {
            end = pathNum;
        }
        SetDecPolicyBatch(fd, g_decPolicyInfos, timestamp, start, end - start);
    }

    close(fd);
    DestroyDecPolicyInfos(g_decPolicyInfos);
    g_decPolicyInfos = NULL;
    return;
}

MODULE_CONSTRUCTOR(void)
{
    APPSPAWN_LOGI("Load sandbox dec module ...");
    AddPreloadHook(HOOK_PRIO_COMMON, SetDenyConstraintDirs);
    AddPreloadHook(HOOK_PRIO_COMMON, SetForcedPrefixDirs);
    AddPreloadHook(HOOK_PRIO_HIGHEST, SetIgnoreCaseDirs);
}