介绍
代码路径:security_access_token: ATM(AccessTokenManager)是OpenHarmony上基于AccessToken构建的统一的应用权限管理能力。
ATM(AccessTokenManager)是OpenHarmony上基于AccessToken构建的统一的应用权限管理能力。
应用的Accesstoken信息主要包括应用身份标识APPID、用户ID,应用分身索引、应用APL(Ability Privilege Level)等级、应用权限信息等。每个应用的Accestoken信息由一个32bits的设备内唯一标识符TokenID(Token identity)来标识。
ATM模块主要提供如下功能:
- 提供基于TokenID的应用权限校验机制,应用访问敏感数据或者API时可以检查是否有对应的权限。
- 提供基于TokenID的Accestoken信息查询,应用可以根据TokenID查询自身的APL等级等信息。
目录介绍
/base/security/access_token
├── frameworks # 框架层,基础功能代码存放目录
│ ├── accesstoken # Accesstoken管理框架代码存放目录
│ ├── tokensync # Accesstoken信息同步框架代码存放目录
│ └── common # 框架公共代码存放目录
├── interfaces # 接口层
│ └── innerkits # 内部接口层
│ ├── accesstoken # Accesstoken内部接口代码存放目录
│ ├── nativetoken # nativetoken内部接口代码存放目录
│ └── tokensync # Accesstoken信息同步内部接口代码存放目录
└── services # 服务层
├── accesstokenmanager # Accesstoken管理服务代码存放目录
└── tokensyncmanager # Accesstoken信息同步服务代码存放目录
基础数据结构
HapTokenInfo
内存中保存token信息的结构,对应到数据库的hap_token_info_table表
class HapTokenInfo final {
public:
ATokenAplEnum apl;
char ver;
int userID;
std::string bundleName;
int instIndex;
std::string appID;
std::string deviceID;
AccessTokenID tokenID;
AccessTokenAttr tokenAttr;
};
appid = 应用包名+证书hash
PermissionPolicySet
存储tokenid和对应权限状态的关系类
TokenID组成
基本类型,tokenid就是一个int型。
typedef unsigned int AccessTokenID;
tokenid主要是如下的四部分组成的
typedef struct {
unsigned int tokenUniqueID : 20;
unsigned int res : 7;
unsigned int type : 2;
unsigned int version : 3;
} AccessTokenIDInner;
数据库结构
hap_token_info_table表
主要存储应用的token信息
列名 | 类型 | 备注 |
token_id | integer | |
user_id | integer | |
bundle_name | text | |
inst_index | integer | |
app_id | text | |
device_id | text | |
apl | integer | |
token_version | integer | |
token_attr | integer |
primary key : token_id
native_token_info_table表
native服务的token信息
列名 | 类型 | 备注 |
token_id | integer | |
process_name | text | |
token_version | integer | |
token_attr | integer | |
dcap | text | |
apl | integer |
primary key : token_id
permission_definition_table表
权限定义表
列名 | 类型 | 备注 |
token_id | integer | |
permission_name | text | |
bundle_name | text | |
grant_mode | integer | |
available_level | integer | |
provision_enable | integer | |
distributed_scene_enable | integer | |
label | text | |
label_id | text | |
description | text | |
description_id | text |
primary key : token_id, permission_name
permission_state_table表
权限状态表
列名 | 类型 | |
token_id | integer | |
permission_name | text | |
device_id | text | |
is_general | integer | |
grant_state | integer | |
grant_flag | integer |
primary key : token_id, permission_name, device_id
启动过程
整个启动过程,主要分为两部分。一部分是去数据库中加载动态的信息,还有一部分就是去系统定义的目录下加载静态的配置文件,静态配置文件主要是native service的token信息。
token的生成
本地应用
应用安装时生成
本地服务
服务自己注册,静态配置文件中读取
远程应用或服务
远程应用访问本地设备时,根据远程应用的设备id,以及远程过来附带的tokenid,进行关系绑定,生成本地的tokenid。
相关本地token和权限的关系,是通过同步机制,从来源设备同步应用权限得到的。
token同步过程
调用点1
分布式组件管理部件
分布式组件管理部件模块负责跨设备组件管理,提供访问和控制远程组件的能力,支持分布式场景下的应用协同。主要功能如下:
- 远程启动元能力:跨设备拉起远端设备上的指定元能力。
- 远程迁移元能力:将元能力跨设备迁移到远端设备。
- 远程绑定元能力:跨设备绑定远端设备上的指定元能力。
./distributedschedule/dmsfwk/services/dtbschedmgr/src/distributed_sched_permission.cpp:177: uint32_t dAccessToken = AccessToken::AccessTokenKit::AllocLocalTokenID(
任务调度过程中,会存在检查权限的场景,该服务会先创建远程tokenid,然后再会检查权限。
数据结构
整个交互过程中,消息中会附带如下的信息
struct CallerInfo {
int32_t uid = -1;
int32_t pid = -1;
int32_t callerType = CALLER_TYPE_NONE;
std::string sourceDeviceId;
int32_t duid = -1;
std::string callerAppId;
std::vector<std::string> bundleNames;
int32_t dmsVersion = -1;
uint32_t accessToken = 0;
};
StartAbilityFromRemote
远程启动应用的时候会去调用权限相关操作
//远程启动应用
int32_t DistributedSchedService::StartAbilityFromRemote(const OHOS::AAFwk::Want& want,
const OHOS::AppExecFwk::AbilityInfo& abilityInfo, int32_t requestCode,
const CallerInfo& callerInfo, const AccountInfo& accountInfo)
{
std::string localDeviceId;
std::string deviceId = want.GetElement().GetDeviceID();
if (!GetLocalDeviceId(localDeviceId) ||
!CheckDeviceIdFromRemote(localDeviceId, deviceId, callerInfo.sourceDeviceId)) {
HILOGE("check deviceId failed");
return INVALID_REMOTE_PARAMETERS_ERR;
}
//检查权限
DistributedSchedPermission& permissionInstance = DistributedSchedPermission::GetInstance();
ErrCode err = permissionInstance.CheckDPermission(want, callerInfo, accountInfo, abilityInfo, deviceId);
if (err != ERR_OK) {
HILOGE("CheckDPermission denied!!");
return err;
}
err = AAFwk::AbilityManagerClient::GetInstance()->Connect();
if (err != ERR_OK) {
HILOGE("connect ability server failed %{public}d", err);
return err;
}
std::vector<int> ids;
ErrCode ret = OsAccountManager::QueryActiveOsAccountIds(ids);
if (ret != ERR_OK || ids.empty()) {
return INVALID_PARAMETERS_ERR;
}
err = AAFwk::AbilityManagerClient::GetInstance()->StartAbility(want, requestCode, ids[0]);
if (err != ERR_OK) {
HILOGE("StartAbility failed %{public}d", err);
}
return err;
}
StartAbilityByCallFromRemote
int32_t DistributedSchedService::StartAbilityByCallFromRemote(const OHOS::AAFwk::Want& want,
const sptr<IRemoteObject>& connect, const CallerInfo& callerInfo, const AccountInfo& accountInfo)
{
HILOGD("[PerformanceTest] DistributedSchedService StartAbilityByCallFromRemote begin");
if (connect == nullptr) {
HILOGE("StartAbilityByCallFromRemote connect is null");
return INVALID_REMOTE_PARAMETERS_ERR;
}
std::string localDeviceId;
std::string destinationDeviceId = want.GetElement().GetDeviceID();
if (!GetLocalDeviceId(localDeviceId) ||
!CheckDeviceIdFromRemote(localDeviceId, destinationDeviceId, callerInfo.sourceDeviceId)) {
HILOGE("StartAbilityByCallFromRemote check deviceId failed");
return INVALID_REMOTE_PARAMETERS_ERR;
}
DistributedSchedPermission& permissionInstance = DistributedSchedPermission::GetInstance();
int32_t result = permissionInstance.CheckGetCallerPermission(want, callerInfo, accountInfo, localDeviceId);
if (result != ERR_OK) {
HILOGE("StartAbilityByCallFromRemote CheckDPermission denied!!");
return result;
}
sptr<IRemoteObject> callbackWrapper;
{
std::lock_guard<std::mutex> autoLock(calleeLock_);
auto itConnect = calleeMap_.find(connect);
if (itConnect != calleeMap_.end()) {
callbackWrapper = itConnect->second.callbackWrapper;
} else {
callbackWrapper = new AbilityConnectionWrapperStub(connect, localDeviceId);
}
}
int32_t errCode = DistributedSchedAdapter::GetInstance().StartAbilityByCall(want, callbackWrapper, this);
HILOGD("[PerformanceTest] StartAbilityByCallFromRemote end");
if (errCode == ERR_OK) {
{
std::lock_guard<std::mutex> autoLock(calleeLock_);
ConnectInfo connectInfo {callerInfo, callbackWrapper, want.GetElement()};
calleeMap_.emplace(connect, connectInfo);
}
connect->AddDeathRecipient(callerDeathRecipient_);
}
return errCode;
}
ConnectAbilityFromRemote
int32_t DistributedSchedService::ConnectAbilityFromRemote(const OHOS::AAFwk::Want& want,
const AppExecFwk::AbilityInfo& abilityInfo, const sptr<IRemoteObject>& connect,
const CallerInfo& callerInfo, const AccountInfo& accountInfo)
{
HILOGD("[PerformanceTest] DistributedSchedService ConnectAbilityFromRemote begin");
if (connect == nullptr) {
HILOGE("ConnectAbilityFromRemote connect is null");
return INVALID_REMOTE_PARAMETERS_ERR;
}
HILOGD("ConnectAbilityFromRemote uid is %{public}d, pid is %{public}d, AccessTokenID is %{public}u",
callerInfo.uid, callerInfo.pid, callerInfo.accessToken);
std::string localDeviceId;
std::string destinationDeviceId = want.GetElement().GetDeviceID();
if (!GetLocalDeviceId(localDeviceId) ||
!CheckDeviceIdFromRemote(localDeviceId, destinationDeviceId, callerInfo.sourceDeviceId)) {
HILOGE("ConnectAbilityFromRemote check deviceId failed");
return INVALID_REMOTE_PARAMETERS_ERR;
}
DistributedSchedPermission& permissionInstance = DistributedSchedPermission::GetInstance();
int32_t result = permissionInstance.CheckDPermission(want, callerInfo, accountInfo, abilityInfo, localDeviceId);
if (result != ERR_OK) {
HILOGE("ConnectAbilityFromRemote CheckDPermission denied!!");
return result;
}
HILOGD("ConnectAbilityFromRemote callerType is %{public}d", callerInfo.callerType);
sptr<IRemoteObject> callbackWrapper = connect;
std::map<sptr<IRemoteObject>, ConnectInfo>::iterator itConnect;
if (callerInfo.callerType == CALLER_TYPE_HARMONY) {
std::lock_guard<std::mutex> autoLock(connectLock_);
itConnect = connectAbilityMap_.find(connect);
if (itConnect != connectAbilityMap_.end()) {
callbackWrapper = itConnect->second.callbackWrapper;
} else {
callbackWrapper = new AbilityConnectionWrapperStub(connect);
}
}
int32_t errCode = DistributedSchedAdapter::GetInstance().ConnectAbility(want, callbackWrapper, this);
HILOGD("[PerformanceTest] ConnectAbilityFromRemote end");
if (errCode == ERR_OK) {
std::lock_guard<std::mutex> autoLock(connectLock_);
if (itConnect == connectAbilityMap_.end()) {
ConnectInfo connectInfo {callerInfo, callbackWrapper};
connectAbilityMap_.emplace(connect, connectInfo);
}
}
return errCode;
}
权限检查过程
/**
* @brief 检查权限
*
* @param tokenID
* @param permissionName
* @return int
*/
int PermissionManager::VerifyAccessToken(AccessTokenID tokenID, const std::string& permissionName)
{
ACCESSTOKEN_LOG_INFO(LABEL, "%{public}s called, tokenID: 0x%{public}x, permissionName: %{public}s", __func__,
tokenID, permissionName.c_str());
//检查权限名是否合法
if (!PermissionValidator::IsPermissionNameValid(permissionName)) {
ACCESSTOKEN_LOG_ERROR(LABEL, "invalid params!");
return PERMISSION_DENIED;
}
std::shared_ptr<HapTokenInfoInner> tokenInfoPtr =
AccessTokenInfoManager::GetInstance().GetHapTokenInfoInner(tokenID);
if (tokenInfoPtr == nullptr) {
ACCESSTOKEN_LOG_ERROR(LABEL, "can not find tokenInfo!");
return PERMISSION_DENIED;
}
//远程的应用,并且本地没有对应权限定义。直接拒绝
if (!tokenInfoPtr->IsRemote() && !PermissionDefinitionCache::GetInstance().HasDefinition(permissionName)) {
ACCESSTOKEN_LOG_ERROR(
LABEL, "no definition for permission: %{public}s!", permissionName.c_str());
return PERMISSION_DENIED;
}
std::shared_ptr<PermissionPolicySet> permPolicySet =
AccessTokenInfoManager::GetInstance().GetHapPermissionPolicySet(tokenID);
if (permPolicySet == nullptr) {
ACCESSTOKEN_LOG_ERROR(LABEL, "invalid params!");
return PERMISSION_DENIED;
}
//根据数据库中加载的授权信息进行权限状态的检查。
return permPolicySet->VerifyPermissStatus(permissionName);
}
AccessTokenID使用
目前看到的tokenid是在binder里面使用的,原本的binder传输的是uid,目前鸿蒙系统中新增了tokenid的传输,目前可以直接从binder里面获取。