PAM的应用开发和内部实现源码分析.doc_第1页
PAM的应用开发和内部实现源码分析.doc_第2页
PAM的应用开发和内部实现源码分析.doc_第3页
PAM的应用开发和内部实现源码分析.doc_第4页
PAM的应用开发和内部实现源码分析.doc_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

PAM 的应用开发和内部实现源码分析本文主要通过对Linux PAM源代码进行分析,阐述了PAM的内部实现机制和怎样在应用程序中应用PAM进行认证,以及怎样开发PAM服务模块。1 引言身份认证是操作系统安全的重要机制之一,系统通过认证机制核查用户的身份证明,并作为用户进入系统的判定条件,是防止恶意用户进入系统的第一道门槛。近年来认证理论和技术得到了迅速发展,产生了各种认证机制,如口令机制,RSA, DCE, kerberos认证体制,S/Key和基于智能卡的身份认证等。然而,当系统中引入新的认证机制时,一些系统入口登录服务如login, rlogin和telnet等应用程序就必须改写以适应新的认证机制。为了解决这个问题,1995年Sun公司的Vipin Samar和 Charlie Lai提出了PAM(Pluggable Authentication Modules),并将其应用在Solaris系统上。PAM框架将应用程序与具体的认证机制分离,使得系统改变认证机制时,不再需要修改采用认证机制的应用程序,而只要由管理员配置应用程序的认证服务模块,极大地提高了认证机制的通用性与灵活性。各种版本操作系统pam实现原理一样,下面从PAM的应用开发开始介绍。2 PAM的应用开发2.1 PAM框架概览PAM即可插拔认证模块。它提供了对所有服务进行认证的中央机制,适用于login,远程登录(telnet,rlogin,fsh,ftp,点对点协议(PPP),su等应用程序中。系统管理员通过PAM配置文件来制定不同应用程序的不同认证策略;应用程序开发者通过在服务程序中使用PAM API(pam_xxxx( )来实现对认证方法的调用;而PAM服务模块的开发者则利用PAM SPI来编写模块(主要是引出一些函数pam_sm_xxxx( )供PAM接口库调用),将不同的认证机制加入到系统中;PAM接口库(libpam)则读取配置文件,将应用程序和相应的PAM服务模块联系起来。PAM框架结构如图所示。图 PAM框架结构图其中,pamh是一个pam_handle类型的结构,它是一个非常重要的处理句柄,是PAM与应用程序通信的唯一数据结构,也是调用PAM接口库API的唯一句柄。pam_handle数据结构将在下面的源代码分析一节的介绍。另外,如上图所示的服务模块分auth(认证管理)、account(账号管理)、session(会话管理)、passwd(口令管理)四种类型,各个类型模块的作用以及配置文件的四个组成部分模块类型、控制标志、模块路径、模块参数等在很多讲PAM的配置管理的文章里都有介绍,这里就不再赘述了。2.2 在应用程序中使用PAM认证每个使用PAM认证的应用程序都以pam_start开始,pam_end结束。PAM还提供了pam_get_item和pam_set_item共享有关认证会话的某些公共信息,例如用户名,服务名,密码和会话函数。应用程序在调用了pam_start ()后也能够用这些APIs来改变状态信息。实际做认证工作的API函数有六个(以下将这六个函数简称为认证API): 认证管理-包括pam_authenticate ()函数认证用户,pam_setcred ()设置,刷新,或销毁用户证书。 账号管理-包括pam_acc_mgmt ()检查认证的用户是否可以访问他们的账户,该函数可以实现口令有效期,访问时间限制等。 会话管理-包括pam_open_session ()和pam_close_session ()函数用来管理会话和记账。例如,系统可以存储会话的全部时间。 口令管理-包括pam_chauthok ()函数用来改变密码。 下面看一个简单的login模拟程序: /* 使用PAM所必需的两个头文件*/#include #include void main(int argc, char *argv, char *renvp) /* 初始化,并提供一个回调函数 */ if (pam_start(login, user_name, &pam_conv, &pamh) != PAM_SUCCESS) exit(1); /* 设置一些关于认证用户信息的参数 */ pam_set_item(pamh, PAM_TTY, ttyn); pam_set_item(pamh, PAM_RHOST, remote_host); while (!authenticated & retry MAX_RETRIES) status = pam_authenticate(pamh, 0);/* 认证,检查用户输入的密码是否正确 */* 认证失败则应用程序退出*/ if (status != PAM_SUCCESS) exit(1); /* 通过了密码认证之后再调用帐号管理API,检查用户帐号是否已经过期 */ if (status = pam_acct_mgmt(pamh, 0) != PAM_SUCCESS) if (status = PAM_AUTHTOK_EXPIRED) status = pam_chauthtok(pamh, 0); /* 过期则要求用户更改密码 */ if (status != PAM_SUCCESS) exit(1); /* 通过帐户管理检查之后则打开会话 */ if (status = pam_open_session(pamh, 0) != PAM_SUCCESS) exit(status); /* 建立认证服务的用户证书*/ status = pam_setcred(pamh, PAM_ESTABLISH_CRED); if (status != PAM_SUCCESS) exit(status); pam_end(pamh, PAM_SUCCESS); /* PAM事务的结束 */ 从上面程序中,我们可以了解到使用PAM认证的一般流程,同时也可以看出PAM API使得使用认证的应用程序不仅不用关心底层使用的服务模块,而且编写起来简洁明了得多。有关开发使用PAM的应用程序更加详细完整的阐述请参考The Linux-PAM Application Developers Guide。2.3 怎样开发PAM服务模块首先在编写的服务模块的源程序里要包含下列头文件:#include PAM的服务模块是一个一个的动态链接库文件(也可以是静态库),PAM接口库通过dlopen来装载这些库。假设源程序名为pam_module-name.c,则需要用下列命令将其编译成动态链接库:gcc -fPIC -c pam_module-name.cld -x -shared -o pam_module-name.so pam_module-name.o选项-fPIC是指位置无关代码(Position Independent Code),这类代码支持大偏移。使用-shared选项将目标代码放进共享目标库中。四种类型的模块各自要实现的函数如下表所示:模块类型要实现的函数函数功能认证管理PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char *argv)认证用户PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char *argv)设置用户证书账号管理PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char *argv)帐号管理会话管理PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char *argv)打开会话PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char *argv)关闭会话口令管理PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char *argv)设置口令当然同一个服务模块可以同时属于多种类型,只要这些类型模块要实现的函数都实现了就可以,比如PAM自带的经典口令认证机制模块pam_unix.so 就可以支持四种模块类型。下面来看一个最简单的pam_deny模块的源程序pam_deny.c:1.#define PAM_SM_AUTH2.#define PAM_SM_ACCOUNT3.#define PAM_SM_SESSION4.#define PAM_SM_PASSWORD5.#include ././libpam/include/security/pam_modules.h6./* - 认证管理函数的实现- */7.PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc8.,const char *argv)9.10. return PAM_AUTH_ERR;11.12.PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc13.,const char *argv)14.15. return PAM_CRED_UNAVAIL;16.17./* - 账号管理函数的实现 - */18.PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc19.,const char *argv)20.21. return PAM_ACCT_EXPIRED;22.23./* - 口令管理函数的实现 - */24.PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc25.,const char *argv)26.27. return PAM_AUTHTOK_ERR;28.29./* - 会话管理函数的实现 - */30.PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc31.,const char *argv)32.33. return PAM_SYSTEM_ERR;34.35.PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc36.,const char *argv)37.38. return PAM_SYSTEM_ERR;39.40./* 模块定义结束 */41./* 静态模块数据 */42.#ifdef PAM_STATIC43.struct pam_module _pam_deny_modstruct = 44. pam_deny,45. pam_sm_authenticate,46. pam_sm_setcred,47. pam_sm_acct_mgmt,48. pam_sm_open_session,49. pam_sm_close_session,50. pam_sm_chauthtok51.;52.#endif很容易看出,pam_deny模块支持四种模块类型。前4行包含静态模块的一些原型申明,第37-39行实现了四种模块类型的函数,因为这只是一个简单的拒绝服务的模块,所以这些函数只是简单地返回认证错或系统错等PAM错误。最后几行定义了该程序被编译成静态模块所需的一个模块数据结构。因此,PAM SPI使得服务模块的开发也相当简单和专一,因为服务模块不再需要考虑和应用程序的交互,只要将自己采用的算法实现好就可以了。3 PAM接口库源代码分析上面我们介绍了怎样使用PAM和怎样开发PAM服务模块,要想对PAM的内部机制有个透彻的理解,还需要进一步分析PAM接口库的代码。下面基于Linux 的pam-0.75-40.src.rpm包所得的源代码进行分析。3.1 PAM接口库主要数据结构先看一下PAM接口库用到的一些主要数据结构。pam_handle和其他几个主要的数据结构(见./libpam/pam_private.h)及其之间的关系如下图所示。其中pam_handle包含认证的用户的token、用户名、应用程序名、终端名等信息,以及一个service结构(handlers);前面几节提到的pamh句柄就是一个pam_handle结构。service结构包含服务模块的相关信息,各个域的含义是:1. module-该结构包含装载的模块的名字、类型(静态或动态模块)、链接句柄(装载模块时的句柄)。2. modules_allocated-分配的模块数。3. modules_used-已使用的模块数。4. handlers_loaded-是否对操作(handlers结构)进行了初始化,handlers结构和初始化handlers见下面的介绍。5. conf-由应用程序相对应的配置文件指定的服务模块的handlers。6. other-为缺省配置文件指定的服务模块的handlers。handlers结构包含六个handler结构链表的指针,六个指针分别对应六种不同的认证API;libpam通过这些指针找到对应模块的SPI服务函数。如下表所示:handler指针API函数SPI函数authenticatepam_authenticate( )pam_sm_authenticate( )setcredpam_setcred( )pam_sm_setcred( )acct_mgmtpam_acct_mgmt( )pam_sm_acct_mgmt( )open_sessionpam_open_session( )pam_sm_open_session( )close_sessionpam_close_session( )pam_sm_close_session( )chauthtokpam_chauthtok( )pam_sm_chauthtok( )handler数据结构是最直接保存服务模块的SPI服务函数的地址及参数的结构,其包含的主要的域的含义如下:1 (*func)-该函数指针指向handlers所装载的服务模块的服务函数。2 argc、*argv-分别为*func所指向的函数的参数个数和参数列表。3 *next-指向堆栈模块中的下一个服务模块的服务函数。由此指针形成所有堆栈模块的服务函数链。3.2 PAM接口库重要内部函数分析PAM接口库中有一系列_pam开头的内部函数,那些APIs主要是调用这些内部函数来完成其功能的。int _pam_add_handler(pam_handle_t *pamh, int must_fail, int other, int type, int *actions, const char *mod_path, int argc, char *argv, int argvlen)该函数负责加载服务模块的SPI函数。这个函数代码很长有300多行,这里就不列举了。其主要步骤如下:1. 根据mod_path提供的模块路径装载服务模块(dl_open)。2. 由type确定的类型来决定要装入的SPI函数名并找到该函数(dlsym)的地址。type类型对应配置文件中的服务模块的四种类型标记,type的值及其对应的要装入的SPI函数名见下表。模块类型typeSPI函数认证管理模块PAM_T_AUTHpam_sm_authenticate pam_sm_setcred 会话管理模块PAM_T_SESSpam_sm_open_session pam_sm_close_session 帐号管理模块PAM_T_ACCTpam_sm_acct_mgmt口令管理模块PAM_T_PASSpam_sm_chauthtok3. 新分配一个handler结构,将dlsym找到的函数的地址赋给该handler结构的func域,并填充其他的结构信息。4. 将新分配的handler结构插入到handlers的对应handler结构链表中。l _pam_parse_conf_file函数负责读并分析PAM配置文件,将相关的信息填充到pamh句柄中,并调用_pam_add_handlers加载服务模块的服务函数。l _pam_init_handlers函数主要做handler的初始化工作,先判断handler是否已初始化,若没有则调用_pam_parse_conf_file分析配置文件加载服务模块的服务函数。l _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,)该函数负责遍历执行模块堆栈中的每一个服务模块对应的SPI函数,即

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论