庫(kù)代碼地址:https://gitee.com/openharmony/security_access_token
以下引用官方介紹:
以下是代碼及主要流程分析:
創(chuàng)建AccessTokenId
通過(guò)隨機(jī)數(shù)創(chuàng)建后保存在
#define TOKEN_ID_CFG_FILE_PATH "/data/service/el0/access_token/nativetoken.json"
https://gitee.com/openharmony/security_access_token/blob/master/interfaces/innerkits/nativetoken/src/nativetoken.c
NativeAtId CreateNativeTokenId(void)
{
uint32_t rand;
NativeAtId tokenId;
AtInnerInfo *innerId = (AtInnerInfo *)(&tokenId);
int ret = GetRandomTokenId(&rand);
if (ret != ATRET_SUCCESS) {
return 0;
}
innerId->reserved = 0;
innerId->tokenUniqueId = rand & (0xFFFFFF);
innerId->type = TOKEN_NATIVE_TYPE;
innerId->version = 1;
return tokenId;
}
使用者:
native進(jìn)程
在native進(jìn)程拉起前,需要調(diào)用GetAccessTokenId函數(shù),獲取該native進(jìn)程的TokenID茄厘;再調(diào)用SetSelfTokenID將進(jìn)程TokenID設(shè)置到內(nèi)核中深浮。
在native進(jìn)程運(yùn)行過(guò)程中,可以通過(guò)調(diào)用GetNativeTokenInfo、CheckNativeDCap來(lái)查驗(yàn)對(duì)應(yīng)進(jìn)程所具備的token信息溅话,包括分布式能力、APL等級(jí)等信息歌焦。
https://gitee.com/openharmony/startup_init_lite/blob/master/services/init/standard/init_service.c
int SetAccessToken(const Service *service)
{
INIT_ERROR_CHECK(service != NULL, return SERVICE_FAILURE, "%s failed", service->name);
int ret = SetSelfTokenID(service->tokenId);
INIT_LOGI("%s: token id %lld, set token id result %d", service->name, service->tokenId, ret);
return ret == 0 ? SERVICE_SUCCESS : SERVICE_FAILURE;
}
void GetAccessToken(void)
{
InitGroupNode *node = GetNextGroupNode(NODE_TYPE_SERVICES, NULL);
while (node != NULL) {
Service *service = node->data.service;
if (service != NULL) {
if (service->capsArgs.count == 0) {
service->capsArgs.argv = NULL;
}
if (strlen(service->apl) == 0) {
(void)strncpy_s(service->apl, sizeof(service->apl), "system_core", sizeof(service->apl) - 1);
}
uint64_t tokenId = GetAccessTokenId(service->name, (const char **)service->capsArgs.argv,
service->capsArgs.count, service->apl);
if (tokenId == 0) {
INIT_LOGE("Get totken id %lld of service \' %s \' failed", tokenId, service->name);
}
service->tokenId = tokenId;
}
node = GetNextGroupNode(NODE_TYPE_SERVICES, node);
}
}
系統(tǒng)調(diào)用入口并寫入內(nèi)核
https://gitee.com/openharmony/security_access_token/blob/master/interfaces/innerkits/token_setproc/src/token_setproc.c
設(shè)備驅(qū)動(dòng)
#define TOKENID_DEVNODE "/dev/access_token_id"
set接口:
int SetSelfTokenID(uint64_t tokenID)
{
int fd = open(TOKENID_DEVNODE, O_RDWR);
if (fd < 0) {
return ACCESS_TOKEN_ERROR;
}
int ret = ioctl(fd, ACCESS_TOKENID_SET_TOKENID, &tokenID);
if (ret) {
close(fd);
return ACCESS_TOKEN_ERROR;
}
close(fd);
return ACCESS_TOKEN_OK;
}
get接口:
uint64_t GetSelfTokenID()
{
uint64_t token = INVAL_TOKEN_ID;
int fd = open(TOKENID_DEVNODE, O_RDWR);
if (fd < 0) {
return INVAL_TOKEN_ID;
}
int ret = ioctl(fd, ACCESS_TOKENID_GET_TOKENID, &token);
if (ret) {
close(fd);
return INVAL_TOKEN_ID;
}
close(fd);
return token;
}
內(nèi)核驅(qū)動(dòng)
../kernel/linux/linux-5.10/drivers/accesstokenid/access_tokenid.c
dev設(shè)備驅(qū)動(dòng)入內(nèi)核層調(diào)用入口
static long access_tokenid_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *uarg = (void __user *)arg;
unsigned int func_cmd = _IOC_NR(cmd);
if (uarg == NULL) {
pr_err("%s: invalid user uarg\n", __func__);
return -EINVAL;
}
if (_IOC_TYPE(cmd) != ACCESS_TOKEN_ID_IOCTL_BASE) {
pr_err("%s: access tokenid magic fail, TYPE=%d\n",
__func__, _IOC_TYPE(cmd));
return -EINVAL;
}
if (func_cmd >= ACCESS_TOKENID_MAX_NR) {
pr_err("%s: access tokenid cmd error, cmd:%d\n",
__func__, func_cmd);
return -EINVAL;
}
if (g_func_array[func_cmd])
return (*g_func_array[func_cmd])(file, uarg);
return -EINVAL;
}
設(shè)置tokenid至進(jìn)程上下文的token字段并保存:
current即為task_struct數(shù)據(jù)結(jié)構(gòu)
int access_tokenid_set_tokenid(struct file *file, void __user *uarg)
{
unsigned long long tmp = 0;
if (!check_permission_for_set_tokenid(file))
return -EPERM;
if (copy_from_user(&tmp, uarg, sizeof(tmp)))
return -EFAULT;
current->token = tmp;
return 0;
}
在設(shè)置前做了一個(gè)簡(jiǎn)單的安全校驗(yàn)check_permission_for_set_tokenid飞几,只有root權(quán)限進(jìn)程才可以設(shè)置
access_token在內(nèi)核的使用:
鴻蒙對(duì)內(nèi)核新增加CONFIG_ACCESS_TOKENID宏控制此feature的使能
可以看到當(dāng)前版本主要是在binder,文件系統(tǒng)和創(chuàng)建進(jìn)程中調(diào)用独撇,目前版本邏輯不多屑墨,
只要是賦值和保存
./include/linux/sched.h:1479:#ifdef CONFIG_ACCESS_TOKENID
./fs/proc/base.c:3311:#ifdef CONFIG_ACCESS_TOKENID
./fs/proc/base.c:3318:#endif /* CONFIG_ACCESS_TOKENID */
./fs/proc/base.c:3436:#ifdef CONFIG_ACCESS_TOKENID
./fs/proc/base.c:3771:#ifdef CONFIG_ACCESS_TOKENID
./drivers/Makefile:195:obj-$(CONFIG_ACCESS_TOKENID) += accesstokenid/
./drivers/android/binder.c:98:#ifdef CONFIG_ACCESS_TOKENID
./drivers/android/binder.c:102:#endif /* CONFIG_ACCESS_TOKENID */
./drivers/android/binder.c:558:#ifdef CONFIG_ACCESS_TOKENID
./drivers/android/binder.c:560:#endif /* CONFIG_ACCESS_TOKENID */
./drivers/android/binder.c:606:#ifdef CONFIG_ACCESS_TOKENID
./drivers/android/binder.c:609:#endif /* CONFIG_ACCESS_TOKENID */
./drivers/android/binder.c:3107:#ifdef CONFIG_ACCESS_TOKENID
./drivers/android/binder.c:3110:#endif /* CONFIG_ACCESS_TOKENID */
./drivers/android/binder.c:4560:#ifdef CONFIG_ACCESS_TOKENID
./drivers/android/binder.c:4565:#endif /* CONFIG_ACCESS_TOKENID */
./drivers/android/binder.c:5167:#ifdef CONFIG_ACCESS_TOKENID
./drivers/android/binder.c:5190:#endif /* CONFIG_ACCESS_TOKENID */
./drivers/accesstokenid/Makefile:2:obj-$(CONFIG_ACCESS_TOKENID) += access_tokenid.o
./kernel/fork.c:877:#ifdef CONFIG_ACCESS_TOKENID
上層使用及相關(guān)邏輯
應(yīng)用hap
在應(yīng)用安裝時(shí),需要調(diào)用AllocHapToken創(chuàng)建獲取該應(yīng)用的TokenID纷铣。
在應(yīng)用運(yùn)行過(guò)程中卵史,需要進(jìn)行鑒權(quán)等操作時(shí),可調(diào)用VerifyAccessToken搜立、GetReqPermissions等函數(shù)查詢校驗(yàn)應(yīng)用權(quán)限以躯、APL等信息。
在應(yīng)用卸載時(shí)啄踊,需要調(diào)用DeleteToken函數(shù)刪除系統(tǒng)中管理的對(duì)應(yīng)Accesstoken信息忧设。
應(yīng)用安裝時(shí)調(diào)用ATM服務(wù)生成AccessTokenId
AccessToken::AccessTokenID BundlePermissionMgr::CreateAccessTokenId(
const InnerBundleInfo &innerBundleInfo, const std::string bundleName, const int32_t userId)
{
APP_LOGD("BundlePermissionMgr::CreateAccessTokenId bundleName = %{public}s, userId = %{public}d",
bundleName.c_str(), userId);
AccessToken::HapInfoParams hapInfo;
hapInfo.userID = userId;
hapInfo.bundleName = bundleName;
hapInfo.instIndex = 0;
hapInfo.appIDDesc = innerBundleInfo.GetProvisionId();
AccessToken::HapPolicyParams hapPolicy = CreateHapPolicyParam(innerBundleInfo);
AccessToken::AccessTokenIDEx accessToken = AccessToken::AccessTokenKit::AllocHapToken(hapInfo, hapPolicy);
APP_LOGD("BundlePermissionMgr::CreateAccessTokenId accessTokenId = %{public}u",
accessToken.tokenIdExStruct.tokenID);
return accessToken.tokenIdExStruct.tokenID;
}
調(diào)用ATM服務(wù)接口
AccessTokenIDEx AccessTokenManagerService::AllocHapToken(const HapInfoParcel& info, const HapPolicyParcel& policy)
{
ACCESSTOKEN_LOG_INFO(LABEL, "%{public}s called", __func__);
AccessTokenIDEx tokenIdEx;
tokenIdEx.tokenIDEx = 0LL;
int ret = AccessTokenInfoManager::GetInstance().CreateHapTokenInfo(
info.hapInfoParameter, policy.hapPolicyParameter, tokenIdEx);
if (ret != RET_SUCCESS) {
ACCESSTOKEN_LOG_INFO(LABEL, "hap token info create failed");
}
return tokenIdEx;
}
針對(duì)Hap包應(yīng)用信息生成AccessToken主要接口
int AccessTokenInfoManager::CreateHapTokenInfo(
const HapInfoParams& info, const HapPolicyParams& policy, AccessTokenIDEx& tokenIdEx)
{
if (!DataValidator::IsUserIdValid(info.userID) || !DataValidator::IsBundleNameValid(info.bundleName)
|| !DataValidator::IsAppIDDescValid(info.appIDDesc) || !DataValidator::IsDomainValid(policy.domain)) {
ACCESSTOKEN_LOG_ERROR(LABEL, "hap token param failed");
return RET_FAILED;
}
AccessTokenID tokenId = AccessTokenIDManager::GetInstance().CreateAndRegisterTokenId(TOKEN_HAP);
if (tokenId == 0) {
ACCESSTOKEN_LOG_INFO(LABEL, "token Id create failed");
return RET_FAILED;
}
std::shared_ptr<HapTokenInfoInner> tokenInfo = std::make_shared<HapTokenInfoInner>(tokenId, info, policy);
if (tokenInfo == nullptr) {
AccessTokenIDManager::GetInstance().ReleaseTokenId(tokenId);
ACCESSTOKEN_LOG_INFO(LABEL, "alloc token info failed");
return RET_FAILED;
}
int ret = AddHapTokenInfo(tokenInfo);
if (ret != RET_SUCCESS) {
ACCESSTOKEN_LOG_WARN(LABEL, "%{public}s add token info failed", info.bundleName.c_str());
AccessTokenIDManager::GetInstance().ReleaseTokenId(tokenId);
return RET_FAILED;
}
ACCESSTOKEN_LOG_INFO(LABEL,
"create hap token 0x%{public}x bundle name %{public}s user %{public}d inst %{public}d ok!",
tokenId, tokenInfo->GetBundleName().c_str(), tokenInfo->GetUserID(), tokenInfo->GetInstIndex());
tokenIdEx.tokenIdExStruct.tokenID = tokenId;
tokenIdEx.tokenIdExStruct.tokenAttr = 0;
RefreshTokenInfoIfNeeded();
return RET_SUCCESS;
}
AccessTokenID AccessTokenIDManager::CreateAndRegisterTokenId(ATokenTypeEnum type)
{
AccessTokenID tokenId = 0;
// random maybe repeat, retry twice.
for (int i = 0; i < MAX_CREATE_TOKEN_ID_RETRY; i++) {
tokenId = CreateTokenId(type);
if (tokenId == 0) {
ACCESSTOKEN_LOG_WARN(LABEL, "create tokenId failed");
return 0;
}
int ret = RegisterTokenId(tokenId, type);
if (ret == RET_SUCCESS) {
break;
} else if (i == MAX_CREATE_TOKEN_ID_RETRY - 1) {
ACCESSTOKEN_LOG_INFO(LABEL, "reigster tokenId failed, maybe repeat, retry");
} else {
ACCESSTOKEN_LOG_WARN(LABEL, "reigster tokenId finally failed");
}
}
return tokenId;
}
創(chuàng)建進(jìn)程時(shí)寫入內(nèi)核
void AppSpawnServer::SetAppAccessToken(const ClientSocket::AppProperty *appProperty)
{
int32_t ret = SetSelfTokenID(appProperty->accessTokenId);
if (ret != 0) {
HiLog::Error(LABEL, "AppSpawnServer::Failed to set access token id, errno = %{public}d", errno);
}
#ifdef WITH_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);
}
#endif
}
調(diào)用及檢查權(quán)限:
如包管理中檢查權(quán)限
foundation/appexecfwk/standard/services/bundlemgr/src/bundle_permission_mgr.cpp:554: int32_t ret = AccessToken::AccessTokenKit::VerifyAccessToken(callerToken, permissionName);
foundation/appexecfwk/standard/services/bundlemgr/src/bundle_permission_mgr.cpp:743: int32_t ret = AccessToken::AccessTokenKit::VerifyAccessToken(tokenId, permissionName);
bool BundlePermissionMgr::VerifyCallingPermission(const std::string &permissionName)
{
APP_LOGD("VerifyCallingPermission permission %{public}s", permissionName.c_str());
AccessToken::AccessTokenID callerToken = IPCSkeleton::GetCallingTokenID();
APP_LOGD("callerToken : %{public}u", callerToken);
AccessToken::ATokenTypeEnum tokenType = AccessToken::AccessTokenKit::GetTokenTypeFlag(callerToken);
if (tokenType == AccessToken::ATokenTypeEnum::TOKEN_NATIVE) {
APP_LOGD("caller tokenType is native, verify success");
return true;
}
int32_t ret = AccessToken::AccessTokenKit::VerifyAccessToken(callerToken, permissionName);
if (ret == AccessToken::PermissionState::PERMISSION_DENIED) {
APP_LOGE("permission %{public}s: PERMISSION_DENIED", permissionName.c_str());
return false;
}
APP_LOGD("verify AccessToken success");
return true;
}
根據(jù)tokenID和permissionName,最終調(diào)用PermissionManager權(quán)限模塊進(jìn)行驗(yàn)證權(quán)限是否定義存在等颠通,進(jìn)行權(quán)限檢查址晕,最后回到AccessTokenInfoManager中根據(jù)tokenid找到某個(gè)應(yīng)用應(yīng)用權(quán)限管理策略中集合,并判斷某個(gè)權(quán)限的情況并返回
int AccessTokenManagerService::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());
return PermissionManager::GetInstance().VerifyAccessToken(tokenID, permissionName);
}
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);
}
看到這里可能有些疑問(wèn)
tokenID和應(yīng)用程序Hap中權(quán)限管理的關(guān)系是什么顿锰?
通過(guò)以下代碼看出谨垃,應(yīng)用的HapTokenInfo中擁有字段permPolicySet_,而permPolicySet_是通過(guò)tokenID和應(yīng)用的權(quán)限集合等參數(shù)生成的對(duì)象
HapTokenInfoInner::HapTokenInfoInner(AccessTokenID id,
const HapInfoParams &info, const HapPolicyParams &policy) : isRemote_(false)
{
tokenInfoBasic_.tokenID = id;
tokenInfoBasic_.userID = info.userID;
tokenInfoBasic_.ver = DEFAULT_TOKEN_VERSION;
tokenInfoBasic_.tokenAttr = 0;
tokenInfoBasic_.bundleName = info.bundleName;
tokenInfoBasic_.instIndex = info.instIndex;
tokenInfoBasic_.appID = info.appIDDesc;
tokenInfoBasic_.deviceID = "0";
tokenInfoBasic_.apl = policy.apl;
permPolicySet_ = PermissionPolicySet::BuildPermissionPolicySet(id, policy.permList, policy.permStateList);
}
所以可以根據(jù)應(yīng)用的tokenID,可以判斷應(yīng)用的某個(gè)權(quán)限狀態(tài)是允許還是拒絕
回想一下Android中的權(quán)限模型設(shè)計(jì)撵儿,frameworks層是以App的UID為單位進(jìn)行權(quán)限判斷
鴻蒙為什么這樣設(shè)計(jì)呢乘客?
int CheckNativeDCap(AccessTokenID tokenID, const std::string& dcap); 檢測(cè)指定tokenID對(duì)應(yīng)的native進(jìn)程是否具有指定的分布式能力
(AccessToken可以跨設(shè)備狐血,可能與分布式權(quán)限管理能力有關(guān))