一、实验目的
本实验在linux系统中使用usb进行身份验证。采用双重登陆方法即输入正确的登陆密码同时还需要插入正确的usb设备才能通过身份认证,以此来实现linux系统登录过程中的安全加固。
二、实验环境
1、系统环境
虚拟机版本:VMware Workstation 16 Pro 16.0.0
Linux版本:2.6.32-131.0.15.el6.x86_64
gcc版本:4.4.4 20100726 (Red Hat 4.4.4-13) (GCC)
发行版本:CentOS 6.1
2、插件及依赖包
PAM及开发工具版本:pam-1.1.1-24.el6.x86_64,pam-devel-1.1.1-24.el6.x86_64
插件工具:pam_usb-0.4.2
依赖包:libxml2, PAM, HAL, pmount
3、安装过程
安装好VMware后,安装CentOS 6的Linux系统。
CentOS 6安装过程。
安装好Linux系统后,可尝试连接usb设备,插入设备,断开与主机的连接,将其与虚拟机连接。usb设备只能同时连接主机或虚拟机中的某一个,不能够同时连接。右下角状态栏也可显示usb设备连接状态。
能用yum找到的包,使用yum install xxx
命令进行安装。其中包括pam、pam-devel、libxml2、hal、hal-devel。
不能用yum找到的包,使用wget下载源代码再进行安装。手动下载pmount的rpm文件,再使用rpm -ivh pmount-0.9.23-1.el6.x86_64.rpm
命令安装pmount包。
安装好上述依赖包后,下载pam-usb插件并解压缩。
进入pam_usb-0.4.2目录下编译包文件,使用make
、make install
命令(见下下图)。查看Makefile文件,编译后首先在文件根目录下生成了pam_usb.so、pamusb-check文件,在/etc/_pamusb_conf路径下生成了pamusb.conf文件用于前期的注册用户及其对应的usb设备。
后续的工作主要通过借助pam-usb插件提供的查找设备等功能,修改其pam.c文件,调用自己的MySecureLogin.c文件,同时修改Makefile文件,生成对应的MySecureLogin.so文件,保存到/lib64/security路径当中,再进行后续的pam.d文档的相关配置。
源文件编译后生成的是pam_usb.so,后续修改c文件生成对应的MySecureLogin.so。
三、 设计思路
1、PAM简介
PAM(Pluggable Authentication Modules)是由Sun提出的一种认证机制。能够提供一系列接口,能够将系统提供的服务于该服务的认证方式分开,由此能够灵活地根据不同的服务来配置不同的认证方式而无需重新修改服务程序的实现代码,也能够为系统添加新的认证手段提供更便捷的方式。
2、pam-usb插件简介
pam-usb插件使用usb来为Linux提供硬件认证。该插件中包含了三个工具:
pamusb-agent: 进行usb设备的获取,检测此时与系统连接的usb设备及详细信息。
pamusb-conf: 配置用户及对应的usb设备,将相关信息加载到统一配置文件。
pamusb-check: 检查某用户认证时需要提供的usb设备及相应信息。
3、pam工具所提供的用户认证接口
pam_authenticate() 函数进行身份认证,在调用该函数前,由pam_start()函数传递用户名、服务名等信息。用户被在输入密码后服务模块中对应的pam_sm_authenticate() 函数会检测用户的输入并验证是否合法。
int pam_authenticate(pam_handle_t *pamh, int flags);
返回值:
PAM_ABORT:程序后续调用pam_end()退出
PAM_AUTH_ERR:用户验证失败
PAM_CRED_INSUFFICIENT:没有足够的凭证来验证用户
PAM_AUTHINFO_UNVAIL:服务模块不能获取用户的验证信息
PAM_MAXTRIES:验证次数达到上限
PAM_SUCCESS:用户验证成功
PAM_USER_UNKNOWN:未找到该用户
int pam_sm_authenticate(pam_handle_t *pamh,
int flags,
int argc,
const char **argv);
返回值:
与pam_authenticate()中的返回值一致。
4、pam_sm_authenticate()中增加usb认证模块
主要通过修改pam_sm_authenticate() 函数,在其中加入usb认证模块,该认证模块提供的服务由pam-usb插件完成,能够帮助检测到usb设备,并将需要使用usb认证的用户及其usb设备注册到conf配置文件中。
在用户认证时,pam_sm_authenticate() 函数的功能就是要检测当前待验证的用户、输入的密码、连接的usb设备,通过比对之前保存的conf配置文件,来判断此次验证是否能通过。
四、 实验步骤
1、注册用户和usb设备信息
借助pam-usb插件在conf文件中配置相关信息:
pamusb-conf: 配置用户及对应的usb设备,将相关信息加载到统一配置文件。
pamusb-check: 检查某用户认证时需要提供的usb设备及相应信息。
1)pamusb-conf配置用户及对应的usb设备:
增加名称为aigo,uuid为7080-C2DE的usb设备:
增加名称为Teclast,uuid为5C3A-DF96的usb设备:
增加用户root,并将其验证usb设备设置为aigo:
增加用户xxx,并将其验证usb设备设置为Teclast:
2)在配置好conf文件后,使用pamusb-check检查设定好的用户和其验证的usb设备连接情况:
检查root用户(此时与其匹配的aigo设备已经接入虚拟机) :
检查xxx用户(此时与其匹配的Teclast设备已经接入虚拟机) :
仅将aigo设备接入虚拟机时,root的设备显示为已连接,xxx的设备显示为未连接:
2、编写MySecureLogin.c
主要修改pam_sm_authenticate() 函数,调用pam-devel提供的开发接口,以及pam-usb提供的设备检查、配置文件接口。
3、编译生成MySecureLogin.so
使用gcc -o MySecureLogin.so -fPIC MySecureLogin.c -Ipam
命令生成.so文件,或修改Makefile文件,使用make、make install命令。
4、系统环境中增加MySecureLogin.so
路径为/lib64/security/MySecureLogin.so。
5、修改pam.d配置文件
路径为/etc/pam.d,应修改login、gdm-password两个文件:
login:对应命令行界面的登录认证模式(/etc/inittab中设置为3)
gdm-password:对应图形界面的登陆认证模式(/etc/inittab中设置为5)
login中在第一行增加auth required MySecureLogin.so
,这样认证时不仅需要用户连接其对应的usb设备,当显示PASS后,还需要再输入正确的密码,才能通过身份认证,由此实现了双重登陆方法。
若设置为auth sufficient MySecureLogin.so
,如果usb认证通过则不需要再输入密码,如果usb认证未通过,密码输入正确也能通过认证。为了令该过程更加安全,采用required配置文件。
gdm-password中也进行相同的配置:
6、实验结果
1) 测试命令行登陆模式
/etc/inittab中设置为3,reboot命令重启系统后,输入用户名root,连接上其对应的usb设备,此时设备显示已连接,但还需要输入密码,第一次密码错误身份验证未通过,第二次密码正确身份验证通过。
如果不连接usb设备,或连接不匹配的设备,设备认证失败,后续密码无论是否正确输入,都不会通过身份认证。
连接xxx用户的usb设备,同时正确输入该用户的密码,能够身份认证通过。
2) 测试图形界面登陆模式
/etc/inittab中设置为5,reboot命令重启系统后,输入用户名xxx,此时没有连接该用户对应的usb设备,即使填写的是正确的密码,仍然显示身份认证失败。
将xxx对应的usb设备Teclast接入后,再次输入正确的密码,此时身份认证成功,能够进入系统中。
五、 实验总结
借助pam-usb插件实现了linux系统登陆的安全加固,在命令行的登陆模式中能较详细地展现每步状态,跟踪认证的情况。但图形界面gdm中封装的登陆界面还不知道如何修改,自己在测试时能知道是什么原因用户认证不通过,但没法在登录的图形界面上显示出如“未连接usb设备”或“输入密码不正确”等提示信息,如果认证失败只能从系统封装好的程序中统一输出“Authentication failure”。
仅在pam.d中,修改了login、gdm-password配置文件,对应了命令行登录和图形界面登录模式。但可以考虑一种情况:对于root用户,xxx用户,如果xxx通过双重认证进入了系统中,想更换到root用户的系统中,可采用su、sudo等命令,这时只是按照原来的输入密码进行验证,也就是并不存在切换用户的过程中需要插入对应的usb设备,为了使该过程更安全可靠,可以在pam.d中修改system-auth配置文件,加入auth required MySecureLogin.so,由此在切换用户的过程中也能实现双重认证的机制。
附录、 实验代码
MySecureLogin.c
#define PAM_SM_AUTH
#include <security/pam_modules.h>
#include <security/_pam_macros.h>
#include "version.h"
#include "conf.h"
#include "log.h"
#include "local.h"
#include "device.h"
PAM_EXTERN
int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
t_pusb_options opts;
const char *service;
const char *user;
const char *tty;
char *conf_file = PUSB_CONF_FILE;
int retval;
pusb_log_init(&opts);
retval = pam_get_item(pamh, PAM_SERVICE,(const void **)(const void *)&service);
if (pam_get_user(pamh, &user, NULL) !=user )
{
log_info("Can't find this user.\n");
return (PAM_AUTH_ERR);
}
log_info("pam-usb anthentication:\n");
log_info("user:%s service:%s\n", user, service);
if (!pusb_local_login(&opts, user))
{
log_error("NO PASS!\n");
return (PAM_AUTH_ERR);
}
if (pusb_device_check(&opts, user))
{
log_info("PASS!\n");
return (PAM_SUCCESS);
}
log_error("NO PASS!\n");
return (PAM_AUTH_ERR);
}
PAM_EXTERN
int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc, const char **argv)
{
return (PAM_SUCCESS);
}
#ifdef PAM_STATIC
struct pam_module _pam_usb_modstruct = {
"pam_usb",
pam_sm_authenticate,
pam_sm_setcred,
NULL,
NULL,
NULL,
NULL
};
#endif
device.c
#include <unistd.h>
#include <string.h>
#include <dbus/dbus.h>
#include <libhal-storage.h>
#include "conf.h"
#include "hal.h"
#include "log.h"
#include "pad.h"
#include "device.h"
static int pusb_device_connected(t_pusb_options *opts, LibHalContext *ctx)
{
char *udi = NULL;
udi = pusb_hal_find_item(ctx,
"storage.serial", opts->device.serial,
"storage.vendor", opts->device.vendor,
"info.product", opts->device.model,
NULL);
log_info("uuid is :%s\n", opts->device.volume_uuid);
if (!udi)
{
udi = pusb_hal_find_item(ctx,
"usb_device.serial", opts->device.serial,
"usb_device.vendor", opts->device.vendor,
"info.product", opts->device.model,
NULL);
if (!udi)
{
log_error("Device \"%s\" is not connected.\n",
opts->device.name);
return (0);
}
}
libhal_free_string(udi);
log_info("USB \"%s\" is connected.\n", opts->device.name);
return (1);
}
int pusb_device_check(t_pusb_options *opts, const char *user)
{
DBusConnection *dbus = NULL;
LibHalContext *ctx = NULL;
int retval = 0;
log_debug("Connecting to HAL...\n");
if (!pusb_device_connected(opts, ctx))
{
pusb_hal_dbus_disconnect(dbus);
libhal_ctx_free(ctx);
return (0);
}
if (opts->one_time_pad)
{
retval = pusb_pad_check(opts, ctx, user);
}
else
{
retval = 1;
}
pusb_hal_dbus_disconnect(dbus);
libhal_ctx_free(ctx);
return (retval);
}