1 关键字

锁屏密码;用户认证

2 问题描述

环境:
开发板:开发者手机
版本号:4.0.10.203

问题现象:
打开 系统设置应用 -> 生物识别和密码 -> 锁屏密码,进入设置锁屏密码界面,根据界面提示输入要设置的密码,在第二遍密码(和第一遍的密码一致)输完后界面无反应、锁屏密码设置失败。

3 问题原因

3.1 正常机制

可在设置锁屏密码界面成功设置锁屏密码,打印日志:

[Settings]: Settings PasswordInputController create password success

3.2 异常机制

在设置锁屏密码界面无法设置锁屏密码,打印日志:

[Settings]: Settings PasswordInputController create password failed

4 解决方案

vendor/hys/laphone/hdf_config/uhdf/device_info.hcs文件内,修改服务pin_auth_interface_service、user_auth_interface_service的uid和gid为对应服务的hostName:

pin_auth :: host {
            hostName = "pin_auth_host";
            priority = 50;
          - uid = "useriam";
          - gid = ["useriam"];
          + uid = "pin_auth_host";
          + gid = ["pin_auth_host"];
...
user_auth :: host {
            hostName = "user_auth_host";
            priority = 50;
          - uid = "useriam";
          - gid = ["useriam"];
          + uid = "user_auth_host";
          + gid = ["user_auth_host"];

*注
hcs配置文件(HDF Configuration Source)是HDF驱动框架的配置描述语言,是为了实现配置代码与驱动代码解耦,以及便于配置的管理而设计的一种Key-Value为主体的文本格式。
hcs配置文件在OpenHarmony源码的vendor目录下,按照芯片厂商、开发板、配置的目录进行规划,HDF驱动的配置位于hdf_config目录下。根据硬件规格,此hdf_config目录下存放内核态配置信息或者分别内核态和用户态的配置信息。
hcs配置文件各字段含义以及配置方法参考:https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-hdf-manage.md#%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86-1

5 定位过程

设置锁屏密码的关键调用链如下:

img

1、应用调用代码

addPinCredential(pinSubType: number, password: string, onResultCall: (result: number) => void): void {
   try {
     ...
     let credentialInfo = {
       credType: AuthType.PIN, credSubType: pinSubType, token: token
     }
     let callback = {
       onResult: (result, extraInfo) => {
         LogUtil.info(`${this.TAG} Add pin credential, result: ${result}`);
         onResultCall(result);
       }
     };
     this.userIdentityManager.addCredential(credentialInfo, callback); 
   }
}

应用端收集credentialInfo参数(认证执行器类型、token),执行userIdentityManager.addCredential录入口令认证凭据(设置密码)

2、userIdentityManager.addCredential调用进入用户IAM子系统napi层流转到统一用户认证框架,在文件base/useriam/user_auth_framework/services/ipc/src/user_idm_service.cpp中的AddCredential函数中,先通过
auto context = ContextFactory::CreateEnrollContext(para, contextCallback); 获取hdi实例创建context,调用 context->Start() 进入用户态驱动

3、执行文件base/useriam/user_auth_framework/services/context/src/enroll_context.cpp的OnStart函数:经由enroll_->Start(scheduleList_, shared_from_this());、drivers/peripheral/user_auth/hdi_service/service/user_auth_interface_service.cpp的UserAuthInterfaceService::BeginEnrollmentV1_1(
int32_t userId, const std::vector &authToken, const EnrollParam &param, ScheduleInfoV1_1 &info) 函数连接user_auth_host服务

4、执行文件base/useriam/user_auth_framework/frameworks/native/executors/src/async_command/enroll_command.cpp内 EnrollCommand::SendRequest函数通过IPC通信调用pin_auth_host服务

5、执行文件drivers/peripheral/pin_auth/hdi_service/service/src/executor_impl.cpp内的函数ExecutorImpl::Enroll(uint64_t scheduleId, const std::vector &extraInfo, const sptr &callbackObj),该函数调用NewSalt(salt) 获取包含'localDeviceId'的uint8_t数组:

uint32_t ExecutorImpl::NewSalt(std::vector<uint8_t> &salt)
{
    IAM_LOGI("start");
    constexpr uint32_t DEVICE_UUID_LENGTH = 65;
    char localDeviceId[DEVICE_UUID_LENGTH] = {0};
    if (GetDevUdid(localDeviceId, DEVICE_UUID_LENGTH) != EC_SUCCESS) { //调用GetDevUdid函数获取localDeviceId
        IAM_LOGE("GetDevUdid failed");
        return HDF_FAILURE;
    }
    ...
    for (uint32_t i = 0; i < size; i++) {
        salt.push_back(result[i]); 
    }    
    return HDF_SUCCESS;
}

6、GetDevUdid最终执行文件base/startup/init/services/modules/udid/udid_comm.c内的INIT_LOCAL_API int GetUdidFromParam(char *udid, uint32_t size)函数获取udid

INIT_LOCAL_API int GetUdidFromParam(char *udid, uint32_t size)
{
    uint32_t len = size;
    int ret = SystemGetParameter("const.product.udid", udid, &len); //获取系统参数const.product.udid
    BEGET_CHECK(ret != 0, return ret);

    len = size;
    ret = SystemGetParameter("const.product.devUdid", udid, &len); //获取系统参数const.product.devUdid
    BEGET_CHECK(ret != 0, return ret);
    return ret;
}

7、SystemGetParameter获取系统参数之前调用文件base/startup/init/services/param/base/param_base.c内的DacCheckParamPermission函数检查DAC访问控制权限:

STATIC_INLINE int DacCheckParamPermission(const ParamLabelIndex *labelIndex,
    const ParamSecurityLabel *srcLabel, const char *name, uint32_t mode)
{    
    /**
     * DAC group
     * user:group:read|write|watch
     */
    ******
    // 2, check uid
    ******
    // 3, check gid
    ******
    // 4, check user in group
    if (CheckUserInGroup(space, node->gid, srcLabel->cred.uid) == 0) {
        localMode = (mode & (DAC_READ | DAC_WRITE | DAC_WATCH)) >> DAC_GROUP_START;
        if ((node->mode & localMode) != 0) {
            return DAC_RESULT_PERMISSION;
        }
    }
    // forbid
    PARAM_LOGW("Param '%s' label gid:%d uid:%d mode 0%x", name, srcLabel->cred.gid, srcLabel->cred.uid, mode);
    PARAM_LOGW("Cfg label %u gid:%d uid:%d mode 0%x ", labelIndex->dacLabelIndex, node->gid, node->uid, node->mode);
    int ret = DAC_RESULT_FORBIDED;
    return ret;
}

依据以上调用链执行到DacCheckParamPermission函数时,对比rk3568功能正常下的日志,发现开发者手机多出了以下Warning日志:

pin_auth_host W [param_base.c:447]Param 'const.product.udid' label gid:1088 uid:1088 mode 0100
pin_auth_host W [param_base.c:448]Cfg label 57992 gid:1053 uid:0 mode 01e8

即开发者手机在执行DacCheckParamPermission函数时最终并没有返回 DAC_RESULT_PERMISSION,通过额外添加日志对比发现,函数体内第4步'检查用户是否在群组'CheckUserInGroup函数的执行结果,开发者手机与rk3568返回不一致:

static int CheckUserInGroup(WorkSpace *space, gid_t groupId, uid_t uid)
{
    char buffer[USER_BUFFER_LEN] = {0};
    int ret = PARAM_SPRINTF(buffer, sizeof(buffer), GROUP_FORMAT, groupId, uid);
    PARAM_CHECK(ret >= 0, return -1, "Failed to format name for "GROUP_FORMAT, groupId, uid);
    PARAM_LOGW("1 CheckUserInGroup gid is: %d,uid is:%d; buffer is:%s", groupId,uid,buffer); // 添加日志
    ParamNode *node = GetParamNode(WORKSPACE_INDEX_BASE, buffer);
    if (node != NULL) {
        return 0;
    }
    return -1;
}

rk3568内添加的日志打印的buffer为 1053_1113,而开发者手机为1053_1088,进程uid不一致,一个为1113,一个是1088,查看进程的uid配置文件base/startup/init/services/etc/passwd,发现对应进程名为pin_auth_host、useriam:

useriam:x:1088:1088:::/bin/false
...
pin_auth_host:x:1113:1113:::/bin/false

再对比相应的hdf框架驱动设备描述配置文件,查看对应服务的配置: vendor/${product_company}${product_name}/hdf_config/uhdf/device_info.hcs发现rk下pin_auth_host服务对应的uid设置的是pin_auth_host,开发者手机下uid则是useriam
gid:1053对应的group为deviceprivate(gid配置文件base/startup/init/services/etc/group

......
deviceprivate:x:1053:root,shell,system,samgr,hdf_devmgr,deviceinfo,dsoftbus,dms,account,pin_auth_host,access_token,device_manager,foundation,dbms,deviceauth,huks_server,dlp_credential,dsserver,edm,update,device_attest

可见deviceprivate组内配置的用户列表里并不包含uid useriam,致开发者手机在执行CheckUserInGroup权限检查时结果为无权限。

而在3.2Release deviceprivate组内配置的用户列表里包含了uid useriam:
https://gitee.com/openharmony/startup_init/blob/OpenHarmony-v3.2.4-Release/services/etc/group#L35
在项目升级到4.0Release时(deviceprivate组内配置的用户已经没有了useriam的uid,相关服务的gid和uid的配置及对应逻辑都做了变动),hcs配置文件却并没有升级,仍然保留了3.2Release的hcs配置文件,因此存在本案例所提到的问题。

6 知识分享

1:本案例针对"设置锁屏密码功能"所涉及用户IAM子系统、启动子系统简要示意图如下:

img

uid、gid等配置项是用户态驱动的启动配置,进程的uid在文件base/startup/init/services/etc/passwd中配置,进程的gid在文件base/startup/init/services/etc/group中配置,进程uid和gid配置参考:系统服务用户组添加方法

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐