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 。
- SELinux 是一组内核修改和用户空间工具,其提供了访问控制安全策略机制,包括了强制访问控制( Mandatory Access Control , MAC )。
- 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新增加模块
文件系统的适配