鸿蒙OpenHarmony SELinux学习

2022年2月20日

注:通过代码走读方式了解流程和概述,部分细节有待完善

代码版本:基于OpenHarmony在线代码https://gitee.com/organizations/openharmony/projects

SELinux主库地址:(https://gitee.com/openharmony/security_selinux)

先抛出问题和疑问:

1,OpenHarmony SELinux模块按照了解是由万里红科技有限公司捐赠并合入主线,根据OpenHarmony的系统特点在各个方面是如何考虑和实现?如应用层,框架层和内核层的自研和适配,裁剪,优化逻辑等

FireShot Capture 004 - 如何为OpenHarmony构筑数字安全万里长城?万里红接受专访发布时间:20_财富号_东方财富网 - caifuhao.eastmoney.com.png

2,从OpenHarmony SELinux介绍中看到,在内核层主要是适配了DBinder和文件系统模块?具体逻辑流程是如何的?

3,OpenHarmony SELinux在kernel层的hook选择,与SELinux原生模块有什么区别?是否保留了完全的hook点与路径?

4,应用程序框架在上层的表现形式与Android app/frameworks区别较大?OpenHarmony SELinux的进程域是如何考虑的和设计的?

5,从OpenHarmony SELinux的构建介绍来看,最后编译出来的是集中式策略文件,和Android上面的 有什么区别?

OpenHarmony SELinux整体架构
整体架构.png
OpenHarmony SELinux目标

目标
SELinux (安全增强式 Linux , Security-Enhanced Linux )是 Linux 历史上杰出的安全子系统。 SELinux SIG 的工作目标是将 SELinux 引入 OpenHarmony 。

  1. SELinux 是一组内核修改和用户空间工具,其提供了访问控制安全策略机制,包括了强制访问控制( Mandatory Access Control , MAC )。
  2. SELinux 已经被添加到各种 Linux 发行版中。其软件架构力图将安全决策的执行与安全策略分离,并简化涉及执行安全策略的软件的数量
AppSpawn进程域设置

App进程域的修改从提交记录看到是于2022-1-26号上传的:
https://gitee.com/openharmony/startup_appspawn/commit/e4eb49f1d924df6fdbb6c4af597d11decdfce271

FireShot Capture 705 - Add_ 支持在配置文件中配置服务进程的绑核、优先级、MAC信息以及AccessToken信息 · e4eb49f · OpenHarmo_ - gitee.com.png

设置接口:

FireShot Capture 707 - Add_ 支持在配置文件中配置服务进程的绑核、优先级、MAC信息以及AccessToken信息 · e4eb49f · OpenHarmo_ - gitee.com.png

可以看到SELinux接口是调用共享库libhap_restorecon,是自研实现的,代码库位于:
base/security/selinux

AppProperty关键结构体定义,其中apl值表示App的SELinux属性

https://gitee.com/openharmony/startup_appspawn/blob/master/interfaces/innerkits/include/client_socket.h

   static constexpr int APPSPAWN_MSG_MAX_SIZE = 4096;  // appspawn message max size
    static constexpr int LEN_PROC_NAME = 256;           // process name length
    static constexpr int LEN_BUNDLE_NAME = 256;         // bundle name length
    static constexpr int LEN_SO_PATH = 256;             // load so lib
    static constexpr int MAX_GIDS = 64;
    static constexpr int APL_MAX_LEN = 32;

    struct AppProperty {
        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[MAX_GIDS];      // a list of UNIX gids that the child process setgroups() to after fork()
        uint32_t gidCount;                // the size of gidTable
        char processName[LEN_PROC_NAME];  // process name
        char bundleName[LEN_BUNDLE_NAME]; // bundle name
        char soPath[LEN_SO_PATH];         // so lib path
        uint32_t accessTokenId;
        char apl[APL_MAX_LEN];
    };

通过代码中看出,App的selinux属性是应用程序框架服务中通过socket传递过来的,并在Appspawn中进行获取解析:

 while (isRunning_) {
        std::unique_lock<std::mutex> lock(mut_);
        dataCond_.wait(lock, [this] { return !this->appQueue_.empty(); });
        std::unique_ptr<AppSpawnMsgPeer> msg = std::move(appQueue_.front());
        appQueue_.pop();
        int connectFd = msg->GetConnectFd();
        ClientSocket::AppProperty *appProperty = msg->GetMsg();
        if (!CheckAppProperty(appProperty)) {
            msg->Response(-EINVAL);
            continue;
        }

        int32_t fd[FDLEN2] = {FD_INIT_VALUE, FD_INIT_VALUE};
        int32_t buff = 0;
        if (pipe(fd) == -1) {
            HiLog::Error(LABEL, "create pipe fail, errno = %{public}d", errno);
            msg->Response(ERR_PIPE_FAIL);
            continue;
        }

缓冲区数据转换:

https://gitee.com/openharmony/startup_appspawn/blob/master/src/appspawn_server.cpp
 ClientSocket::AppProperty *AppSpawnMsgPeer::GetMsg() const
40  {
41      return reinterpret_cast<ClientSocket::AppProperty *>(buf_.get());
42  }

而在系统框架服务这一侧,App的selinux属性则是通过AppSpawnStartMsg中的apl值来传递的:

https://gitee.com/openharmony/aafwk_standard/blob/weekly_20220215/services/appmgr/include/app_spawn_msg_wrapper.h
namespace OHOS {
namespace AppExecFwk {
struct AppSpawnStartMsg {
    int32_t uid;
    int32_t gid;
    std::vector<int32_t> gids;
    std::string procName;
    std::string soPath;
    uint32_t accessTokenId;
    std::string apl;
    std::string bundleName;
};

aafwk框架服务中通过applicationInfo中的appPrivilegeLevel标签赋值,与Android非常类似,可能也是通过安装时匹配签名与se配置文件规则得出应用进程的所属域:

https://gitee.com/openharmony/aafwk_standard/blob/weekly_20220215/services/appmgr/src/app_mgr_service_inner.cpp

    startMsg.uid = (*bundleInfoIter).uid;
    startMsg.gid = (*bundleInfoIter).gid;
    startMsg.accessTokenId = (*bundleInfoIter).applicationInfo.accessTokenId;
    startMsg.apl = (*bundleInfoIter).applicationInfo.appPrivilegeLevel;
    startMsg.bundleName = bundleName;
    APP_LOGD("StartProcess come, accessTokenId: %{public}d, apl: %{public}s, bundleName: %{public}s",
        startMsg.accessTokenId, startMsg.apl.c_str(), bundleName.c_str());

    bundleMgrResult = bundleMgr_->GetBundleGidsByUid(bundleName, uid, startMsg.gids);
    if (!bundleMgrResult) {
        APP_LOGE("GetBundleGids is fail");
        return;
    }

如OpenHarmony中的sehap_contexts对应Android中的system/sepolicy/private/seapp_contexts类似

https://gitee.com/openharmony/security_selinux/blob/master/sepolicy/sehap_contexts

apl=system_core domain=platform_hap_domain type=platform_hap_data_file
apl=system_basic domain=priv_hap_domain type=priv_hap_data_file
apl=normal domain=normal_hap_domain type=normal_hap_data_file

回到设置进程域的部分:
hap_restorecon.cpp这个类的主要作用是提供设置进程域的接口HapDomainSetcontext,通过解析规则文件,最后通过setcon系统调用走到kernel层设置。

base/security/selinux/interfaces/policycoreutils/src/hap_restorecon.cpp

int HapContext::HapDomainSetcontext(const std::string &apl, const std::string &packageName)
{
    if (apl.empty()) {
        return -SELINUX_ARG_INVALID;
    }

    if (is_selinux_enabled() < 1) {
        SELINUX_LOG_INFO(LABEL, "Selinux not enbaled");
        return SELINUX_SUCC;
    }

    char *typeContext = nullptr;
    if (getcon(&typeContext)) {
        return -SELINUX_GETCON_ERR;
    }

    context_t con = nullptr;
    con = context_new(typeContext);
    if (con == nullptr) {
        return -SELINUX_PTR_NULL;
    }
    char *oldTypeContext = typeContext;

    int res = HapContextsLookup(true, apl, packageName, con);
    if (res < 0) {
        freecon(oldTypeContext);
        context_free(con);
        return res;
    }

    typeContext = context_str(con);
    if (typeContext == nullptr) {
        freecon(oldTypeContext);
        context_free(con);
        return -SELINUX_PTR_NULL;
    }

    SELINUX_LOG_INFO(LABEL, "Hap type for %{public}s is changing from %{public}s to %{public}s", packageName.c_str(),
                     oldTypeContext, typeContext);

    if (security_check_context(typeContext) < 0) {
        freecon(oldTypeContext);
        context_free(con);
        return -SELINUX_TYPE_INVALID;
    }

    if (strcmp(typeContext, oldTypeContext)) {
        if (setcon(typeContext) < 0) {
            freecon(oldTypeContext);
            context_free(con);
            return -SELINUX_SETCON_ERR;
        }
    }
    SELINUX_LOG_INFO(LABEL, "Hap setcon finish for %{public}s", packageName.c_str());

    freecon(oldTypeContext);
    context_free(con);
    return SELINUX_SUCC;
}
OpenHarmony SELinux Kernel层的变化(待续)

DBinder新增加模块
文件系统的适配

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容