库代码地址:https://gitee.com/openharmony/security_access_token
以下引用官方介绍:
以下是代码及主要流程分析:
创建AccessTokenId
通过随机数创建后保存在
#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进程
在native进程拉起前,需要调用GetAccessTokenId函数,获取该native进程的TokenID;再调用SetSelfTokenID将进程TokenID设置到内核中。
在native进程运行过程中,可以通过调用GetNativeTokenInfo、CheckNativeDCap来查验对应进程所具备的token信息,包括分布式能力、APL等级等信息。
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);
}
}
系统调用入口并写入内核
https://gitee.com/openharmony/security_access_token/blob/master/interfaces/innerkits/token_setproc/src/token_setproc.c
设备驱动
#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;
}
内核驱动
../kernel/linux/linux-5.10/drivers/accesstokenid/access_tokenid.c
dev设备驱动入内核层调用入口
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;
}
设置tokenid至进程上下文的token字段并保存:
current即为task_struct数据结构
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;
}
在设置前做了一个简单的安全校验check_permission_for_set_tokenid,只有root权限进程才可以设置
access_token在内核的使用:
鸿蒙对内核新增加CONFIG_ACCESS_TOKENID宏控制此feature的使能
可以看到当前版本主要是在binder,文件系统和创建进程中调用,目前版本逻辑不多,
只要是赋值和保存
./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
上层使用及相关逻辑
应用hap
在应用安装时,需要调用AllocHapToken创建获取该应用的TokenID。
在应用运行过程中,需要进行鉴权等操作时,可调用VerifyAccessToken、GetReqPermissions等函数查询校验应用权限、APL等信息。
在应用卸载时,需要调用DeleteToken函数删除系统中管理的对应Accesstoken信息。
应用安装时调用ATM服务生成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;
}
调用ATM服务接口
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;
}
针对Hap包应用信息生成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;
}
创建进程时写入内核
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
}
调用及检查权限:
如包管理中检查权限
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;
}
根据tokenID和permissionName,最终调用PermissionManager权限模块进行验证权限是否定义存在等,进行权限检查,最后回到AccessTokenInfoManager中根据tokenid找到某个应用应用权限管理策略中集合,并判断某个权限的情况并返回
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);
}
看到这里可能有些疑问
tokenID和应用程序Hap中权限管理的关系是什么?
通过以下代码看出,应用的HapTokenInfo中拥有字段permPolicySet_,而permPolicySet_是通过tokenID和应用的权限集合等参数生成的对象
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);
}
所以可以根据应用的tokenID,可以判断应用的某个权限状态是允许还是拒绝
回想一下Android中的权限模型设计,frameworks层是以App的UID为单位进行权限判断
鸿蒙为什么这样设计呢?
int CheckNativeDCap(AccessTokenID tokenID, const std::string& dcap); 检测指定tokenID对应的native进程是否具有指定的分布式能力
(AccessToken可以跨设备,可能与分布式权限管理能力有关)