鸿蒙3.1 基于Token的访问控制

介绍

代码路径: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里面获取。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值