2022年2月19日
注:通过代码走读方式了解流程和概述,部分细节有待完善
代码版本:基于OpenHarmony在线代码https://gitee.com/organizations/openharmony/projects
先抛出问题和疑问:
1,OpenHarmony应用程序安全底座是什么?应用整体安全性有哪些方面,是如何保证的?
2,OpenHarmony内核层是基于Linux,应用程序安全模型应该与Android系统的应用程序基础安全模型差异不大?但是目前版本可能存在不完善的地方和差异,从kernel->frameworks->app各个层面具体差异在哪?设计思想差异等
内核子系统:采用多内核(Linux内核或者LiteOS)设计,支持针对不同资源受限设备选用适合的OS内核。内核抽象层(KAL,Kernel Abstract Layer)通过屏蔽多内核差异,对上层提供基础的内核能力,包括进程/线程管理、内存管理、文件系统、网络管理和外设管理等。
3,OpenHarmony应用程序启动流程是如何的?
4,OpenHarmony应用程序沙盒机制的安全性设计是如何实现的?与Android系统差异性如何?
以下问题待研究学习,不在此文章
5,OpenHarmony应用程序是否有组件安全?数据安全等如何保证?
6,OpenHarmony应用程序运行时载入机制具体流程?
7,OpenHarmony应用程序的IPC机制如何?如何保证安全性?
8,宏内核与微内核在安全性方面区别?微内核裁剪了哪些安全机制
8,JS技术栈在目前鸿蒙系统中是否新引入风险?是如何保证的?
9,分布式跨端拉起FA除了设备认证外是如何保证安全性的?是否涉及到分布式权限管理?
OpenHarmony代码库与应用程序启动有关库(搜索startup关键字)
以下主要分析starup_appspawn应用孵化模块这个库相关代码
appspawn应用孵化器组件
查看官方介绍
所以AppSpwan类似于Android中的Zygote,负责对OpenHarmony中的应用程序启动等工作,同样也是采用本地Socket方式进行通讯。
以下是核心代码:
代码路径位于:https://gitee.com/openharmony/startup_appspawn/blob/master/src/appspawn_server.cpp
ServerMain函数中创建进程:
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;
}
InstallSigHandler();
pid_t pid = fork();
if (pid < 0) {
HiLog::Error(LABEL, "AppSpawnServer::Failed to fork new process, errno = %{public}d", errno);
close(fd[0]);
close(fd[1]);
msg->Response(-errno);
continue;
} else if (pid == 0) {
SpecialHandle(appProperty);
return SetAppProcProperty(connectFd, appProperty, longProcName, longProcNameLen, fd);
}
read(fd[0], &buff, sizeof(buff)); // wait child process resutl
close(fd[0]);
close(fd[1]);
HiLog::Info(LABEL, "child process init %{public}s", (buff == ERR_OK) ? "success" : "fail");
(buff == ERR_OK) ? msg->Response(pid) : msg->Response(buff); // response to AppManagerService
socket_->CloseConnection(connectFd); // close socket connection
HiLog::Debug(LABEL, "AppSpawnServer::parent process create app finish, pid = %{public}d", pid);
}
return false;
}
设置UID/GID
// set gid
if (setresgid(gid, gid, gid) == -1) {
HiLog::Error(LABEL, "setgid(%{public}u) failed: %{public}d", gid, errno);
return (-errno);
}
// If the effective user ID is changed from 0 to nonzero, then all capabilities are cleared from the effective set
if (setresuid(uid, uid, uid) == -1) {
HiLog::Error(LABEL, "setuid(%{public}u) failed: %{public}d", uid, errno);
return (-errno);
}
return ERR_OK;
}
还有设置权能,进程名称,应用沙盒数据目录等,此部分与Android代码非常类似,同时可以看到OpenHarmony的应用程序安全基础模型也是基于Linux UID/GID/权能等,即基于Linux安全模型,每个应用都是相互隔离的。
OpenHarmony的应用程序数据沙盒目录:
/mnt/sandbox/<packagename>
/mnt/sandbox/<packagename>/data/storage
/data/accounts/account_0/applications/
/data/accounts/account_0/appdata/
/data/storage/el1
同时可以看到通过WITH_SELINUX宏控制设置进程SELINUX属性:
HapContext hapContext;
ret = hapContext.HapDomainSetcontext(appProperty->apl, appProperty->processName);
if (ret != 0) {
HiLog::Error(LABEL, "AppSpawnServer::Failed to hap domain set context, errno = %{public}d", errno);
}
走读一下OpenHarmony应用启动流程(详细描述用时序图,此处省略)
应用启动流程主要流程在于 用户程序框架子系统,代码路径https://gitee.com/openharmony/appexecfwk_standard
通过用户程序框架子系统appexecfwk的框架图看一下AppSpawn模块位置:
1, 从Launcher系统桌面点击图标开始启动应用
startAbility类似Android中的startActivity
代码路径位于Launcer工程中的:
./common/src/main/ets/default/manager/LauncherAbilityManager.ets
import featureAbility from '@ohos.ability.featureAbility';
/**
* 启动应用
*
* @params paramAbilityName Ability名
* @params paramBundleName 应用包名
*/
public startLauncherAbility(paramAbilityName, paramBundleName) {
console.info('Launcher ==> LauncherAbilityManager startApplication abilityName => ' + paramBundleName + "bundleName");
let result = featureAbility.startAbility({
want: {
bundleName: paramBundleName,
abilityName: paramAbilityName
}
}).then((data) => {
console.info("Launcher ==> LauncherAbilityManager startApplication promise::then : " + JSON.stringify(data));
}).catch((error) => {
console.info("Launcher ==> LauncherAbilityManager startApplication promise::catch : " + JSON.stringify(error));
});
console.info("Launcher ==> LauncherAbilityManager startApplication AceApplication : startAbility : " + result);
}
}
2,通过NAPI接口调用到用户框架子系统服务真正操作函数逻辑
(NAPI类似于JNI,中间还经过js framework,js engine)
OpenHarmony-v3.0-LTS/foundation/aafwk/standard/interfaces/kits/napi/aafwk/featureAbility/feature_ability.cpp
3,Ability元能力框架子系统中流程
(类似Android中的AMS)
https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/%E5%85%83%E8%83%BD%E5%8A%9B%E5%AD%90%E7%B3%BB%E7%BB%9F.md
[https://gitee.com/openharmony/aafwk_standard/blob/master/services/abilitymgr/src/ability_manager_client.cpp](https://gitee.com/openharmony/aafwk_standard/blob/master/services/abilitymgr/src/ability_manager_client.cpp)
ErrCode AbilityManagerClient::StartAbility(const Want &want, int requestCode, int32_t userId)
{
CHECK_REMOTE_OBJECT_AND_RETURN(remoteObject_, ABILITY_SERVICE_NOT_CONNECTED);
sptr<IAbilityManager> abms = iface_cast<IAbilityManager>(remoteObject_);
return abms->StartAbility(want, userId, requestCode);
}
ErrCode AbilityManagerClient::StartAbility(
const Want &want, const sptr<IRemoteObject> &callerToken, int requestCode, int32_t userId)
{
CHECK_REMOTE_OBJECT_AND_RETURN(remoteObject_, ABILITY_SERVICE_NOT_CONNECTED);
HILOG_INFO("%{public}s called, bundleName=%{public}s, abilityName=%{public}s, userId=%{public}d",
__func__, want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), userId);
sptr<IAbilityManager> abms = iface_cast<IAbilityManager>(remoteObject_);
return abms->StartAbility(want, callerToken, userId, requestCode);
}
ErrCode AbilityManagerClient::StartAbility(const Want &want, const AbilityStartSetting &abilityStartSetting,
const sptr<IRemoteObject> &callerToken, int requestCode, int32_t userId)
{
CHECK_REMOTE_OBJECT_AND_RETURN(remoteObject_, ABILITY_SERVICE_NOT_CONNECTED);
sptr<IAbilityManager> abms = iface_cast<IAbilityManager>(remoteObject_);
return abms->StartAbility(want, abilityStartSetting, callerToken, userId, requestCode);
}
[https://gitee.com/openharmony/aafwk_standard/blob/master/services/abilitymgr/src/ability_manager_service.cpp](https://gitee.com/openharmony/aafwk_standard/blob/master/services/abilitymgr/src/ability_manager_service.cpp)
int AbilityManagerService::StartAbility(const Want &want, const sptr<IRemoteObject> &callerToken,
int32_t userId, int requestCode)
{
BYTRACE_NAME(BYTRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
auto flags = want.GetFlags();
if ((flags & Want::FLAG_ABILITY_CONTINUATION) == Want::FLAG_ABILITY_CONTINUATION) {
HILOG_ERROR("StartAbility with continuation flags is not allowed!");
return ERR_INVALID_VALUE;
}
HILOG_INFO("%{public}s", __func__);
if (CheckIfOperateRemote(want)) {
HILOG_INFO("AbilityManagerService::StartAbility. try to StartRemoteAbility");
return StartRemoteAbility(want, requestCode);
}
HILOG_INFO("AbilityManagerService::StartAbility. try to StartLocalAbility");
return StartAbilityInner(want, callerToken, requestCode, -1, userId);
}
int AbilityManagerService::StartAbilityInner(const Want &want, const sptr<IRemoteObject> &callerToken,
int requestCode, int callerUid, int32_t userId)
{
BYTRACE_NAME(BYTRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
HILOG_DEBUG("%{public}s begin.", __func__);
if (callerToken != nullptr && !VerificationAllToken(callerToken)) {
HILOG_ERROR("%{public}s VerificationAllToken failed.", __func__);
return ERR_INVALID_VALUE;
}
LoadAbility的时候发现如何应用进程没有起来则通过socket和AppSpwan连接,发送参数给孵化器创建应用进程。
void AppMgrServiceInner::LoadAbility(const sptr<IRemoteObject> &token, const sptr<IRemoteObject> &preToken,
const std::shared_ptr<AbilityInfo> &abilityInfo, const std::shared_ptr<ApplicationInfo> &appInfo,
const std::shared_ptr<AAFwk::Want> &want)
{
BYTRACE_NAME(BYTRACE_TAG_APP, __PRETTY_FUNCTION__);
if (!CheckLoadabilityConditions(token, abilityInfo, appInfo)) {
APP_LOGE("CheckLoadabilityConditions failed");
return;
}
if (!appRunningManager_) {
APP_LOGE("appRunningManager_ is nullptr");
return;
}
BundleInfo bundleInfo;
HapModuleInfo hapModuleInfo;
if (!GetBundleAndHapInfo(*abilityInfo, appInfo, bundleInfo, hapModuleInfo)) {
APP_LOGE("GetBundleAndHapInfo failed");
return;
}
std::string processName;
MakeProcessName(processName, abilityInfo, appInfo);
APP_LOGI("processName = [%{public}s]", processName.c_str());
auto appRecord =
appRunningManager_->CheckAppRunningRecordIsExist(appInfo->name, processName, appInfo->uid, bundleInfo);
if (!appRecord) {
appRecord =
CreateAppRunningRecord(token, preToken, appInfo, abilityInfo, processName, bundleInfo, hapModuleInfo);
if (!appRecord) {
APP_LOGI("CreateAppRunningRecord failed, appRecord is nullptr");
return;
}
StartProcess(abilityInfo->applicationName, processName, appRecord,
abilityInfo->applicationInfo.uid, abilityInfo->applicationInfo.bundleName);
} else {
StartAbility(token, preToken, abilityInfo, appRecord, hapModuleInfo);
}
PerfProfile::GetInstance().SetAbilityLoadEndTime(GetTickCount());
PerfProfile::GetInstance().Dump();
PerfProfile::GetInstance().Reset();
}
void AppMgrServiceInner::StartProcess(const std::string &appName, const std::string &processName,
const std::shared_ptr<AppRunningRecord> &appRecord, const int uid, const std::string &bundleName)
{
BYTRACE_NAME(BYTRACE_TAG_APP, __PRETTY_FUNCTION__);
if (!remoteClientManager_->GetSpawnClient() || !appRecord) {
APP_LOGE("appSpawnClient or apprecord is null");
return;
}
auto bundleMgr_ = remoteClientManager_->GetBundleManager();
if (bundleMgr_ == nullptr) {
APP_LOGE("GetBundleManager fail");
return;
}
auto userId = GetUserIdByUid(uid);
AppSpawnStartMsg startMsg;
BundleInfo bundleInfo;
std::vector<AppExecFwk::BundleInfo> bundleInfos;
bool bundleMgrResult = bundleMgr_->GetBundleInfos(AppExecFwk::BundleFlag::GET_BUNDLE_WITH_ABILITIES,
bundleInfos, userId);
if (!bundleMgrResult) {
APP_LOGE("GetBundleInfo is fail");
return;
}
auto isExist = [&bundleName, &uid](const AppExecFwk::BundleInfo &bundleInfo) {
return bundleInfo.name == bundleName && bundleInfo.uid == uid;
};
auto bundleInfoIter = std::find_if(bundleInfos.begin(), bundleInfos.end(), isExist);
if (bundleInfoIter == bundleInfos.end()) {
APP_LOGE("Get target fail.");
return;
}
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;
}
startMsg.procName = processName;
startMsg.soPath = SO_PATH;
PerfProfile::GetInstance().SetAppForkStartTime(GetTickCount());
pid_t pid = 0;
ErrCode errCode = remoteClientManager_->GetSpawnClient()->StartProcess(startMsg, pid);
if (FAILED(errCode)) {
APP_LOGE("failed to spawn new app process, errCode %{public}08x", errCode);
appRunningManager_->RemoveAppRunningRecordById(appRecord->GetRecordId());
return;
}
APP_LOGI("newPid:%{public}d uid:%{public}d", pid, startMsg.uid);
appRecord->GetPriorityObject()->SetPid(pid);
appRecord->SetUid(startMsg.uid);
OptimizerAppStateChanged(appRecord, ApplicationState::APP_STATE_CREATE);
appRecord->SetAppMgrServiceInner(weak_from_this());
OnAppStateChanged(appRecord, ApplicationState::APP_STATE_CREATE);
AddAppToRecentList(appName, appRecord->GetProcessName(), pid, appRecord->GetRecordId());
OnProcessCreated(appRecord);
PerfProfile::GetInstance().SetAppForkEndTime(GetTickCount());
}
总结:
1,OpenHarmony应用程序安全模型基于Linux UID/GID基础安全模型
2,从OpenHarmony应用程序沙盒逻辑中看到有mount namespace数据隔离等,是否支持动态存储权限待继续研究
3,OpenHarmony应用程序安全模型缺少底层应用进程资源控制SetRLimits,原因未明,可能未考虑手机等富设备支持,并且基于LiteOs内核设计
4,OpenHarmony在线代码中看到SELINUX已并入主线(具体再开章节研究),并且支持应用进程域