Linux身份鉴别机制模块代码分析报告.docx
- 文档编号:23785991
- 上传时间:2023-05-20
- 格式:DOCX
- 页数:55
- 大小:329.12KB
Linux身份鉴别机制模块代码分析报告.docx
《Linux身份鉴别机制模块代码分析报告.docx》由会员分享,可在线阅读,更多相关《Linux身份鉴别机制模块代码分析报告.docx(55页珍藏版)》请在冰豆网上搜索。
Linux身份鉴别机制模块代码分析报告
Linux身份鉴别机制模块代码分析报告
1总体描述
1.1概述
Linux身份鉴别机制是保护操作系统安全的重要机制之一,是防止恶意用户进入系统的一个重要环节。
早期的身份鉴别机制就是传统的UNIX身份鉴别机制,它采用口令加密并与原密码进行对比的方式来对用户身份进行鉴别。
但是这种加密方式过于单一,在一个服务中用户的帐号密码泄露会涉及到多个服务的安全性,所以为了增强系统的安全性,出现了许多其他的身份鉴别机制,如指纹认证、USB认证等。
但是这样导致了一个问题,为了应用这些认证机制,就需要重新编写并编译应用程序(如系统登陆服务login)。
为了解决这个问题,1995年Sun公司的VipinSamar和CharlieLai提出了PAM(PluggableAuthenticationModules)身份鉴别机制,它采用模块化设计和插件功能,使得系统在更改认证机制时不再需要修改应用程序,极大的提高了认证机制的灵活性。
本报告对Linux各用户帐号的权限区别进行了分析,对传统UNIX身份鉴别机制的实现过程进行了研究,重点对PAM身份鉴别机制的实现过程进行了研究与分析,最后通过一个具体的PAM策略演示场景实现了身份鉴别机制的执行过程,研究结果也发现Linux身份鉴别机制是在Linux用户态下实现的,并不涉及内核的具体实现。
1.2涉及到的源码范围说明
由于Linux身份鉴别机制是在用户态下实现,本报告涉及的源码包括Linux-PAM-1.1.6、openpam和Linuxsu命令的实现。
具体范围说明如下:
Linux-PAM-1.1.6/moudles/pam_access.c:
登录认证模块的实现源码。
Linux-PAM-1.1.6/libpam:
PAM所用的头文件。
openpam/lib/pam_acct_mgmt.c:
账号管理接口函数实现。
Linuxsu命令:
Linux系统命令源码包。
1.3技术方案及原理
Linux身份鉴别机制就是对请求服务的用户身份进行鉴别,并且赋予相应的权限的过程。
本文通过查阅资料,分析Linux系统中对各用户帐号的管理及其权限分配,分析传统的UNIX身份鉴别机制,以su命令的代码实现来进行说明,着重分析PAM身份鉴别机制,并对其代码实现来进行分析说明。
1.3.1Linux中用户帐号管理
在Linux系统中,系统设置了多个帐号和组来进行管理,每个帐号都具有不同的权限,例如超级用户root就具有最大的权限,对所有的文件具有读写执行的权限,但是普通用户对某些文件只具有读有时甚至连读的权限都没有。
Linux操作系统并不是以帐号名称来识别用户的,而是以标识符UID和GID来辨别用户的,这些帐号的信息都存放在/etc/passwd文件当中,该文件中记录的每个帐号信息是以行来表现的,如下所示:
root:
x:
0:
0:
root:
/root:
/bin/bash
每行是以7个字段来描述的,每个字段以冒号来进行分隔,其中对应字段的依次解释如下:
1.帐号名称:
用来对应UID。
2.密码:
所有的用户都有对该文件的访问权限,为了防止密码窃取,就将这个字段的密码数据改放到/etc/shadow中了,所以这里显示的是X。
3.UID:
用户标识符,表1-1列出了redhat系统中UID号的限制。
表1-1UID限制【1】
Id范围
该用户ID特性
0(系统管理员)
当UID为0时,代表这个帐号是“系统管理员”!
所以当你要让其他的帐号名称也具有root权限时,将该帐号的UID改为0即可。
一个系统上面的系统管理员不见得只有root.不过不建议有多个。
1~499(系统帐号)
保留给系统使用的ID,其实除了0之外,其他的UID权限与特性并没有不一样。
默认500以下的数字让给系统作为保留帐号只是一个习惯。
1~99:
由distributions自行创建的系统帐号;100~499:
若用户有系统帐号需求时,可以使用的帐号UID。
500~65535
给一般用户使用的
4.GID:
用户组标识符,具体信息存放在/etc/group中。
5.用户信息说明列:
解释这个帐号的意义。
6.主文件夹:
即用户的主文件夹。
7.Shell:
当用户登录系统后就会取得一个shell来与系统的内核通信以进行用户的操作任务(一个shell可以用来替代成让帐号无法取得shell环境的登录操作/sbin/nologin)。
/etc/shadow中也是这样的字段,具体字段的意义这里不再做详细解释,这个文件主要是为了增加系统安全性而另外设置用来存放用户的密码,只有root用户才有权限访问,但是为了安全性显示的仍然是加密后的密码。
系统是通过UID、GID来对不同的帐号进行区分的,但是系统中是如何对应实现具体的权限设置的呢?
这里就从文件的权限来进行说明。
执行ls-al命令后,结果如图1-1所示。
图1-1文件属性
每行显示内容分为七列:
第一列代表这个文件的类型与权限,第二列表示有多少文件名链接到这个节点,第三列表示这个文件的所有者的帐号,第四列表示这个文件的所属用户组,第五列表示这个文件的容量大小,默认单位为B,第六列为该文件的创建文件日期或者是最近的修改日期,第七列为该文件名。
第一列中总共有10个字符,这10个字符解释如下【1】:
1.第一个字符为[d],表示为目录,为[-]则是文件,为[l]则是链接文件,为[b]则表示设备文件里面的可供存储的接口设备,为[c]则表示设备文件里面的串行端口设备,比如键盘、鼠标等。
2.接下来都是以三个字符为一组,一共有三组,且每组都是以“rwx”这样的形式组合,其中[r]代表刻度,[w]代表可写,[x]代表可执行,如果没有该权限则会用[-]来表示。
第一组是文件所有者的权限,第二组为同组的权限,第三组为其他非本用户组的权限。
这样就把文件的可读属性与不同用户帐号的权限对应起来了。
1.3.2传统的UNIX身份鉴别机制原理
传统的UNIX身份鉴别即口令认证方式,它主要通过识别用户的用户名或者UID号获取在/etc/shadow中存放的对应用户密码密文等信息,然后获取用户输入密码并采用crypt()函数对获得的输入密码进行加密,当然加密方式是与原用户密码的加密方式是一致的,然后将这两个密文进行比较,如果一致则通过验证,如果不一致则拒绝赋予权限。
接下来以登录Linux系统为例,说明登录过程中传统UNIX认证方式对用户帐号的鉴别过程。
Linux用户在登录主机时会出现一个输入账号及密码的界面,Linux会先找寻/etc/passwd里面是否有这个账号,如果拒绝用户登录请求,如果有的话则将该账号对应的UID、GID、该账号的主目录与shell设定一并读出,接着就是将用户输入密码进行加密,然后读取/etc/shadow中存放的密码,将这个密码与用户输入并加密的密码进行核对,如果核对成功,则进入系统。
Linux提供getpwnam()来获取用户登录的相关信息,返回的是一个passwd型的结构体,该结构体就包含了用户帐号的各种信息,该结构体如下所示。
structpasswd{
char*pw_name;//用户名
char*pw_passwd;//密码
uid_tpw_uid;//UID
gid_tpw_gid;//GID
char*pw_gecos;//用户信息
char*pw_dir;//主文件夹目录
char*pw_shell;//shell程序}
1.3.3PAM身份鉴别机制原理
PAM(PluggableAuthenticationModules)即可插拔验证模块,它是一个非常完善的身份验证机制,它采用模块化设计和插件功能,从而可以轻易的在应用程序中插入新的鉴别模块或者替换原来的组件,而不必对应用程序做任何修改,从而使软件的定制、维持和升级更加轻松。
PAM的易用性较强,它对上层屏蔽了鉴别的具体细节,用户不必详细知道各种鉴别机制是如何实现的,它还实现了多鉴别机制的集成问题。
1.3.3.1PAM框架
PAM为了实现其插件功能和易用性,采取了分层设计思想。
就是让各鉴别模块从应用程序中独立出来,然后通过PAM的API、SPI作为两者联系的纽带,这样应用程序就可以根据需要灵活地在其中“插入”所需要的鉴别功能模块,从而真正实现了在认证和鉴别基础上的随需应变。
PAM框架如下图所示:
图1-2PAM框架图
其中的配置文件(etc/pam.d)是由系统管理员来设置的,用以制定认证策略。
当应用程序调用PAM的API时,应用接口层按照PAM配置文件的定义来加载相应的认证鉴别模块,然后把请求(即从应用程序那里得到的参数)传递给底层的认证鉴别模块,这时认证鉴别模块就可以根据要求执行具体的认证鉴别操作了。
当认证鉴别模块执行完相应的操作后,再将结果返回给应用接口层,然后由接口层根据配置的具体情况将来自认证鉴别模块的应答返回给应用程序。
上面简单描述了PAM的运作方式,下面对它的三个层次加以介绍:
1.最上层为应用程序层:
使用PAM机制的应用程序(如login、su等),调用PAM接口库的上层接口API来实现认证功能;
2.中间为应用接口层:
连接应用程序和服务模块的中间层,它根据配置文件中的设置加载相应的服务模块,将请求传递到具体的服务模块,向应用程序的开发者提供API使用各种鉴别模块,向鉴别模块的开发者提供SPI来挂载新的鉴别模块;
3.最下层为服务模块:
服务模块中都为动态链接库,它给应用程序提供具体的认证用户服务,应用程序可以使用几个服务模块。
PAM所支持的四种任务管理:
account类型表示账户管理,它执行账户管理,它主要用来限制/允许用户对某个服务的访问时间,当前有效的系统资源(最多可以有多少个用户),限制用户的位置(例如:
root用户只能从控制台登录);auth类型表示认证管理,对用户进行认证。
让应用程序提示用户输入密码或者其它的标记,确认用户的合法性;通过它的凭证许可权限,设定组成员关系或者其它优先权;password类型表示口令管理,用于更新与用户相关的认证特性,如更改用户密码;session类型表示会话管理,用于进入给定的服务之前,或者离开给定的服务之后,需要进行的会话操作,如:
存储关于与用户交换数据的log信息、挂接目录等。
【3】
1.3.3.2PAM的配置文件
应用PAM的服务例程是通过读取PAM的配置文件初始化pam_handle这个句柄,用来记录整个PAM的过程信息例如服务名、接口传递参数等,定义服务的配置文件存放在/etc/pam.d中,/etc/pam.conf也是pam的配置文件,该配置文件是老版本的配置文件,目前所安装的版本当中,如果有pam.d这个文件就首要读取这个文件,若没有再读取pam.conf这个文件,另外若所读服务不存在相应配置文件,此时系统会读取other这个配置文件的信息,other这个配置文件是所有服务的缺省选项。
pam.d中的配置文件的语法形式如下:
module-typecontrol-flagmudle-patharguments【3】
1.module-type:
linuxPAM所提供的四种服务模块,包括auth、account、session、password;
2.control-flag:
控制标志用来设置验证成功或者失败后PAM需要做出的反应,有四个关键词如下所示:
required:
表示即使某个模块对用户的验证失败,也要等所有的模块执行完成,PAM才可以返回错误信息,如此用户就不知道被哪个模块拒绝了。
requisite:
一旦模块对用户的验证失败,PAM立马返回出错信息。
sufficient:
一旦模块对用户的验证成功,PAM立马返回成功信息,并把控制权交给用户。
optional:
PAM会忽略这个模块所产生的验证错误,继续执行下一个模块。
3.module-path:
PAM验证模块的路径;
4.arguments:
传递给模块的参数。
参数是可选择的,可以是读取另外配置文件的路径信息,也可以是各种模块所需的传递参数。
以下列出pam_unix模块的参数信息:
表1-2pam_unix模块参数【3】
参数
说明
debug
打开调试信息,通过syslog写入日志系统
audit
比debug记录更为详细的调试信息
nullok
默认值下,如果用户输入的口令为空,则不允许用户访问服务,该参数覆盖率这个默认值,即使不输入口令就可以获得服务
nodelay
取消延迟,默认是2秒为延迟时间
try_first_pass
提示用户使用前面模块认证通过的口令,只适用于auth类型
use_first_pass
强制使用前面模块认证通过的口令,只适用于authpassword类型
not_authok
当口令改变时,强制模块设置新口令到以前模块提供的口令
md5
当用户下次改变他们的口令时,使用md5算法加密口令
shadow
维护shadow口令,即使用/etc/shadow存储口令
unix
当用户更改密码时,密码被放置在/etc/passwd中
bigcrype
当用户下次改变口令时,使用DECC2算法加密口令
nis
使用NISRPC(远程过程调用)设置新口令
Remember=x
将每个用户最后x个口令存储在/etc/security/opasswd文件中,用于强制口令更改历史,以免用户频繁使用同一个口令
broken_shadow
在account管理模块中读取shadow口令时忽略错误
pam_conf配置文件的写法与pam.d中的配置文件没有太大的出入,只是在最前面多了一行具体的服务名称,例如login,而pam.d中是将各种模块分开来写,这样更便于控制。
1.3.3.3PAM接口函数
PAM提供了多种接口函数以供用户使用,其中包括框架API【4】、服务类型的API和服务模块SPI。
框架API:
每个支持PAM机制的应用程序在使用PAM机制之前必须调用函数pam_start()初始化PAM上下文和PAM事务,使用完后调用pam_end()来结束PAM机制,通过pam_get_item、pam_set_item()来读写PAM事物的状态信息,通过pam_get_data()、pam_set_data()来取得和设置PAM模块及会话的相关信息,通过pam_putenv()、pam_getenv()、pam_getenvlist()来读写环境变量,通过pam_strerror()返回相关的错误信息;
服务类型的API:
1.认证管理模块通过pam_authenticate()对用户名和密码进行认证,通过pam_setcred()用来修改用户的信任参数,建立数字认证;
2.账户管理模块通过pam_acct_mgmt()来检查用户本身是否具有权限登录系统、账户是否过期、账户是否有登录时间限制等;
3.密码管理模块通过pam_chauthtok()来修改用户的密码;
4.会话管理模块通过pam_open_session()来开始会话,以pam_close_session()来结束会话;
PAM中的API与SPI详细解释如表1-3所示。
表1-3PAM中API以及SPI【3】
服务管理类型
PAMAPI
PAMSPI
说明
account
pam_acct_mgmt()
pam_sm_acct_mgmt()
检查认证特性、账户有效期和验证访问控制
authentication
pam_authenticate()
pam_sm_authenticate()
根据认证特征认证用户
pam_setcred()
pam_sm_setcred()
在认证后会话前调用,用来修改用户的信任参数,如:
uidgid
passwd
pam_chauthtok()
pam_sm_chauthtok()
用于改变给定用户的认证特征
session
pam_open_session()
pam_sm_open_session()
为认证成功的用户建立会话
pam_close_session()
pam_sm_open_session()
结束会话
2详细分析
2.1模块功能描述
在系统中对用户进行身份验证可以说是系统安全性保护的第一道门槛,只有通过了用户身份验证,系统才会赋予不同的权限给用户,用户才能使用该系统资源。
其结构图如图2-1所示。
图2-1身份鉴别过程结构图
各应用程序(如login)对系统提出请求,系统将读取服务信息根据配置文件来选择对其身份进行鉴别的机制,根据判别身份而决定是否给该应用程序授权。
2.2模块内部函数调用关系
在传统UNIX鉴别方式中,函数调用如下所示:
图2-2UNIX鉴别方式过程图
1.首先通过getpwnam()来获取需要进行验证的用户信息;
2.再通过getspnam()获得/etc/shadow中用户的信息;
3.再通过getpass()来获取用户所输入的密码;
4.在/etc/shadow中所存放的用户帐号密码是经过加密的,所以当获得用户输入密码之后要通过crypt()这个函数来对密码进行同等方式的加密;
5.最后将加密的结果与存放于/etc/shadow中的密文进行比较,若一致则通过验证。
PAM验证机制中个接口函数与应用程序与验证服务模块函数的调用关系如下图所示:
图2-3PAM接口函数关系
1.应用程序通过调用PAM所提供的API调用服务模块;
2.验证服务模块通过PAM所提供的SPI挂载相应验证过程;
3.每个接口都对应四种服务类型,调用某种类型的API,就应该使用对应类型的服务模块,而对于服务模块来说,同样的,调用某种类型的SPI,就应该属于某种服务类型,在配置文件的编写中就需要注意是否是属于该服务类型。
2.3函数实现机制
Linux中传统的UNIX验证方式中,crypt()函数是一个非常重要的函数,它是GNU标准库里面的函数,默认它是基于数据加密标准(DataEncryptionStandard)算法,但是GNU也对这个函数进行了扩展,也可以使用MD5等其他的加密方式来进行加密。
该函数原型为char*crypt(constchar*key,constchar*salt);其中key就是要被加密的密码字符串,salt这个值是用来对密码字符串进行加密,不同的salt值就会产生不同的密码。
从/etc/shadow中可以看出其密码字段的格式就是$id$salt$encrypted-string,id取值为1的时候采用MD5的方式进行加密,取值为2a则是采用Blowfish的方式进行加密,取值为5采用SHA-256的方式进行加密,取值为6时采用SHA-512的方式进行加密。
salt就是上面所说的salt,encrypted-string就是加密后的密文。
下面介绍两种加密方式中salt的取值方式。
在DES的加密方式中,salt是从[a-zA-Z0-9./]中选择的一个包含两个字符的字符串,这个字符串用来以4096种不同的方法对这个算法进行初始化。
这个算法使用密码中前8个字符的低7位来生成一个密钥。
这个密钥用来反复对一个常量字符串(包含0)进行转换。
返回值是一个包含13个可打印字符的字符串,用来表示加密后的密码。
但是这种方式加密的密码在当前的计算机发展能力下是可以通过并行计算机对整个密钥空间遍历搜索得到的。
在MD5的加密方式中,salt是一个以3个字符"$1$"开始的字符串,后面是前8个字(可以以"$"结束)并输出34个字节的内容。
具体来说,这些字节是$1$[string]$[encrypted-string],其中[string]代表salt中"$1$"后面的8个字符,后面是22个从[a-zA-Z0-9./]集合中选出的字符。
通过分析passwd.c得知具体salt的生成是通过crypt_make_salt来生成的,其函数定义为char*crypt_make_salt(constchar*meth,void*arg);通过传递的meth这个参数来确定是采用哪种加密方式,从而生成不同的salt长度传递给gensalt()这个函数,由该函数随机产生一组固定长度的字符串。
salt就是随机产生的一组字符串,然后经过相应的加密算法来对密码进行加密生成密文。
在PAM验证机制中提供了各种接口函数给用户使用,从而调用模块来实现验证,查看openpam源码中pam_acct_mgmt()接口函数的调用过程,其调用层次图如下所示:
图2-4pam_acct_mgmt调用层次图
PAM的API通过服务分发函数openpam_dispatch将服务定向到具体的服务模块,并检查当前服务模块函数是否为空,如果不为空则表示该模块正在被使用中,需要停止操作,这样可以防止同一模块重复调用,接着会遍历整个服务模块栈,然后调用服务模块的具体函数。
可以看出PAM只为具体的服务模块提供一个接口,并在头文件中定义好一个供函数调用的钩子函数int(*pam_func_t)(structpam_handle*,int,int,constchar**);从而调用到具体的服务模块函数。
pam_handle_t*pamh是贯穿整个pam的重要结构,是pam机制各种信息的从入口点,它描述了整个PAM过程,该结构定义于libpam/pam_private.h中,具体定义如下:
structpam_handle{
char*authtok;
unsignedcaller_is;
structpam_conv*pam_conversation;
char*oldauthtok;
char*prompt;/*forusebypam_get_user()*/
char*service_name;
char*user;
char*rhost;
char*ruser;
char*tty;
char*xdisplay;
char*authtok_type;/*PAM_AUTHTOK_TYPE*/
structpam_data*data;
structpam_environ*env;/*structuretomaintainenvironmentlist*/
struct_pam_fail_delayfail_delay;/*helperfunctionforeasydelays*/
structpam_xauth_dataxauth;/*authinfoforXdisplay*/
structservicehandlers;
struct_pam_former_stateformer;/*librarystate-supportfor
eventdrivenapplications*/
constchar*mod_name;/*Nameofthemodulecurrentlyexecuted*/
intmod_argc;/*Numberofmodulearguments*/
char**mod_argv;/*modulearguments*/
intchoice;/*Whichfunctionwecallf
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 身份 鉴别 机制 模块 代码 分析 报告