1.双因子认证介绍
登录 Linux 系统时,需要输入账户的密码,这种只通过密码这种单一的认证方式我们称之为单因子认证。出于安全考虑,在用户登录时,不仅要输入密码还需要有某个可以认证身份的硬件或者其他的口令等,这种成为双因子或多因子认证,比如我们手机银行转账时一天内转账超过5000元,需要插入 u 盾, u 盾是一个可以验证你身份的硬件。
本文介绍利用 Linux-PAM 技术,实现除了密码认证外的 ukey 认证。开启双因子认证后,用户登录时需要密码后,回车验证密码是否正确,但不管验证正确与否都继续验证 ukey,最后再将最终验证结果返回给用户,这是 PAM 的功能。
2.ukey 接口介绍
本文介绍的 ukey 是我司自主研发,集成国密算法、商用密码算法。具有加密解密、签名验签、随机数生成器等能力。
ukey设备常用的接口如下:
enum ,遍历当前系统插入多少个ukey设备。
open ,除 enum 、 close 接口外,其他任何对 ukey 的操作前都需要打开 ukey 设备,该接口返回一个 handle 。
verify_pin ,验证ukey的PIN码,这是实现双因子认证的重要接口,用户需要输入正确的pin码才能完成认证。
change_pin ,修改pin码,就想用户修改密码一样,区别在于pin码保存在ukey内部。
get_dev_info ,获得 ukey 的 sn 号, sn 号具有唯一性。
gen_random ,通过 ukey 内部的随机数生成器产生随机数。
sm2_sign_ex ,利用 ukey 的 sm2 算法对数据进行签名。
sm2_sign_verify_ex ,利用 ukey 的 sm2 算法对数据进行验签,需要传入公钥。
read_sm2_pubkey ,从 ukey 中获得 sm2 算法的公钥,并妥善保存。
close ,对 ukey 操作完成后,关闭 ukey 设备。
3.keyservice 服务介绍
keyservice 服务是以 root 权限运行在后台的 dbus 系统服务,是对 ukey 接口的封装。客户端登录时,发送 dbus 请求给 keyservice 来帮助客户端完成身份的认证,并返回认证结果给客户端,这样有助于权限的管控,比如在 SELinux 开启的情况下,一般的进程,即使是以 root 权限的进程也是没有权限访问 ukey 设备的, keyservice 服务的存在,只需要对 keyservice 配置 SELinux 策略即可。
keyservice 服务还具有绑定 ukey 、解绑 ukey 以及修改 ukey 的 pin 码等功能。账户要想使用双因子认证,必须绑定唯一的一个 ukey ,在一个系统上 ukey 的用户名是一一对应的。 keyservice 接到绑定请求后,读取 ukey 的串号以及 sm2 算法的公钥,保存在 /etc/usbkey/*** 文件中, *** 就是以用户名为文件名的文件。 keyservice 在接收到解绑请求后,先验证用户输入的 pin 码的正确性,如果正确的话就将绑定信息 /etc/usbkey/*** 文件删除。 keyservice 接收到修改 pin 码指令后,先验证用户输入的原始 pin 码的正确性,如果正确的话就将用户新输入的 pin 码设置到 ukey 中。目录 /etc/usbkey 通过 MAC 控制只有 keyservice 能够访问,保证了绑定信息存储的安全性,就像 /etc/shadow 文件只能 unix_chkpwd 和 unix_update 两个程序能够检查和修改。
4.pam_usb 模块实现
pam_usb 以一个独立的 rpm 包存在,包里面包括 pam_usb.so 模块,提供 PAM 机制使用,还包含了 getpubkey(绑定 ukey)、 rmpubkey(解绑 ukey)以及 chpinpw(修改 UKEY 的 pin 码)等工具。
4.1.pam_usb.so 实现
创建 pam_module 结构体:
struct pam_module _pam_usb_modstruct = {
"pam_usb",
pam_sm_authenticate,
pam_sm_setcred,
NULL,
NULL,
NULL,
NULL
};
其中 pam_sm_setcred 接口直接返回PAM_SUCCESS。pam_sm_authenticate 实现 ukey 认证的逻辑, ukey 认证逻辑流程如下图所示:
4.2.绑定 ukey 流程
4.2.解绑 ukey 流程
4.2.修改 pin 码流程
5.应用
修改 pam 文件,配置在用户登录时使用除了密码认证外的 ukey 认证。 CentOS 的终端登录使用的配置文件是 /etc/pam.d/system-auth ,该文件通过 include 或者 substack 两个关键字被其他配置文件引用,如: su 、 login 等.
默认情况下, system-auth 的 auth 模块组只是用密码认证的方式,如下所示:
auth sufficient pam_unix.so nullok try_first_pass
当用户登录时通过 pam_unix 模块对用户密码进行校验,如果校验正确立即返回认证成功,否则继续往下执行,这是 sufficient 关键字的作用。对PAM机制不理解的,可以看下我的博客Linux-PAM机制。
现在除了密码验证外增加 ukey 认证,修改如下:
auth required pam_unix.so null try_first_pass
auth sufficient pam_usb.so
我们把 pam_unix 模块的认证模式改为了 required ,目的是不管密码认证成功与否,都继续执行下面的 pam_usb 模块的认证。 pam_usb 模块的认证模式是 sufficient ,目的是如果 pam_usb 模块认证成功,且 pam_unix 模块认证也成功,则立即返回认证成功,用户登录成功。如果 pam_usb 和 pam_unix 两个模块有一个失败则本次认证结果返回认证失败,而且用户不知道是哪个模块认证失败了,用户登录失败。