Chinaunix首页 | 论坛 | 博客
  • 博客访问: 56750
  • 博文数量: 20
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 96
  • 用 户 组: 普通用户
  • 注册时间: 2015-07-18 17:00
文章分类

全部博文(20)

文章存档

2017年(6)

2016年(14)

我的朋友

分类: LINUX

2016-05-25 16:04:20

----|    介绍
此文主要介绍了PAM模块的知识,最后我还加上了一个PAM的简单使用例子,希望有用。
PAM的英文全称是Pluggable Authentication Module系统,即此程序是有关执行
用户鉴别和帐号维护的服务。鉴别部分通常通过一(合法性)质询-回应的交互
来完成的。使用PAM,管理员可以通过不重编辑鉴定程序来定制一些使用方法。
PAM有四部分组成,第一部分是libpam,是实现PAM API的库,第二部分是PAM配置
文件,/etc/pam.conf,第三部分有一套动态可装载两进位对象组成,常常用来调
用一些处理实际鉴别(authentication)工作的服务模块。最后模块是使用PAM API
的系统命令组成,如login,us,ftp,telnet,etc...
----| LIBPAM
PAM API的认证(authentication)常规程序有三个主要函数组成:
pam_start( const char *service_name, const char *username,
           const struct pam_conv *conv, pam_handle_t **pamh_p );
pam_end( pam_handle_t *pamh, int exit_status );
pam_authenticate( pam_handle_t *pamh, int flags );
pam_start()和pam_end()函数是开始和结束一个PAM会话,传递给pam_start()
函数的参数如下所示:
+ service_name: 一定义在pam.conf中的特殊服务(请看下面)
+ username:     需要鉴权的用户登录名。
+ conv:         一指向pam_conf结构的指针
+ pamh_p:       一双精度指向pam_handle_t结构的指针。PAM构架会分配或不
分配内存给这个结构,并且一应用程序不能直接访问它。它基本上是用来通过PAM
构架(framework)来处理多个并发的PAM会话。
pam_conv结构如下所示:
struct pam_conv {
    int (*conv)(int num_msg, const struct pam_message **msg,
                struct pam_response **resp, void *appdata_ptr);
    void *appdata_ptr;
}
*conv是指向PAM对话函数的指针,它将会在下面详细讨论,appdata_ptr指针指
向特殊应用程序数据,它并不常用。
pam_end()函数的参数由在pam_start()函数中填充的pam_handle_t*组成,并且返回
的是exit状态。exit状态正常情况下是PAM_SUCCESS。pam_edn()会收回与
pam_handle_t*相关联的内存,并且任何企图重用这个句柄将返回一个seg fault
错误。
pam_authenticate()函数也再一次由通过pam_start()填充的pam_handle_t*组成,
并且可选的标志(flags)可以传递给结构(framework)
另外一些可以应用于应用程序的PAM API函数如下所示(可以参看你的系统文档):
+ pam_set_item() - 为PAM会话写状态信息
+ pam_get_item() - 为PAM获得状态信息
+ pam_acct_mgmt() - 检查当前用户帐号是否合法
+ pam_open_session() - 开始一个新的会话
+ pam_close_session() - 关闭当前会话进程
+ pam_setcred() -管理用户信任资格(credentials)
+ pam_chauthtok() - 改变用户的鉴权(authentication)token牌
+ pam_strerror() - 返回错误字符串,类似与perror()函数
----| PAM.CONF
PAM配置文件通常位于/etc/pam.conf,它可以分为四个部分:鉴权(authentication),
帐号管理,会话管理和密码管理。一个标准的一行配置如下所示:
login  auth  required  /usr/lib/security/pam_unix.so.1  try_first_pass
第一栏是服务名,这是参照在pam_start()函数中的第一个参数。如果通过pam_start()
的服务请求不列在pam.conf,则将使用默认的"other"服务。"other"服务名字可以
是"su"和"rlogin",如果服务名不止一次说明,模块将会提示"stacked",并且
framework将会通过第三栏的值来决定。
第二栏指示这特定的服务将执行何种类型的行为,合法的值是:
"auth"为鉴权行为;
"account"为帐号管理;
"session"为会话管理;
"password"为密码管理;
不是所有的应用程序需要使用每一个行为,如,su仅仅需要使用"auth"鉴权行为,
"passwd"只需要来使用"password"管理行为。
第三栏作为一个控制类型的一栏。如果用户在鉴定行为中失败,它指示PAM 架构
(framework)行为。此栏正确的值为:"requisite","required", "sufficient", "optional":
+ "requisite"意思指如果用户在鉴定(quthentication)时失败,PAM framework
会立即返回一失败信息,其中没有其他模块调用。
+ "required"指示如果一个用户鉴定(quthentication)时失败,PAM framework
只在调用其他所有模块后在返回失败信息。这样做的话用户会不知道哪个模块
鉴权被拒绝,如果一个用户成功被鉴别,所有"required"模块必须返回成功。
+ "optional"意思是用户将被允许访问即使鉴权失败,失败的结果是下一个在
堆栈中的模块将被处理。
+ "sufficient"指的是如果用户传递一这特定模块,PAM framework会立即返回
成功,即使随后的模块有"requisite"或者"required"控制值,类似于"optional"
"sufficient"回允许访问即使鉴全步骤失败。
    Note that if any module returns success, the user will succeed authentication
with the only exception being if the user previously failed to authenticate
with a "required" module.
在pam.conf第四栏中是认证模块的路径,各个系统路径不同,如在Linux-PAM系统
中PAM模块在/usr/lib目录下,而Solaris在/usr/lib/security中维护模块。
第五栏是一个空格分开的module-dependent选项列表,是传递给认证模块调用。
---| 模块 MODULES
每一个PAM模块本质上是一个必须输出特定函数的库,这些函数可以被PAM framework
调用,通过库输出的函数有如下列表:
    + pam_sm_authenticate()
    + pam_sm_setcred()
    + pam_sm_acct_mgmt()
    + pam_sm_open_session()
    + pam_sm_close_session()
    + pam_sm_chauthtok()
如果实现者不决定在一模块内支持特定的操作,模块会为此操作返回PAM_SUCCESS,
例如:如果一个模块设计成为不支持帐号管理(account management),
pam_sm_acct_mgmt()函数会简单的返回PAM_SUCCESS。
pam_sm_authenticate()是按下面的方式声明的:
extern int pam_sm_authenticate( pam_handle_t *pamh, int flags,
       int argc, char **argv);
上面的指针是指想一个PAM句柄--已经通过framework填充了,flags是应用程序
调用pam_authenticate()传递给framework的一组标志,argc和argv是在pam.conf
中此服务的选项参数的数字和值。
一个简单的pam_unix 模块中的pam_sm_authenticate()函数应该如下所示:
#include
#include <...>
extern int
pam_sm_authenticate( pam_handle_t *pamh, int flgs, int c, char **v )
{
        char *user;
        char *passwd;
        struct passwd *pwd;
        int ret;
        /* ignore flags and optional arguments */
        if ( (ret = pam_get_user( ..., &user )) != PAM_SUCCESS )
           return ret;
        if ( (ret = pam_get_pass( ..., &passwd )) != PAM_SUCCESS )
           return ret;
        if ( (pwd = getpwnam(user)) != NULL ) {
           if ( !strcmp(pwd->pw_passwd, crypt(passwd)) )
              return PAM_SUCCESS;
           else
              return PAM_AUTH_ERR;
        }
        return PAM_AUTH_ERR;
}
当然,这个函数非常单纯化,但它演示了pam_sm_authenticate()函数的基本功能。
它从Framework中获得用户的LOGIN名字和密码,在获得用户加密的密码,最后调用
crypt()函数并把结果和加密了的系统密码进行比较。pam_get_*()函数调用Framework,
----|应用程序 APPLICATION
一个应用程序处理PAM部分必须由pam_start()和pam_end()组成和一PAM对话函数。
比较幸运的是,user-space PAM API定义的比较成熟和稳定所以对话函数是比较
模板型的代码(至少对命令行应用程序)。一个简单的su PAM实现所下所示:
#include
#include <...>
int su_conv(int, const struct pam_message **,
            struct pam_response **, void *);
static struct pam_conv pam_conv = { su_conv, NULL };
int
main( int argc, char **argv )
{
        pam_handle_t *pamh;
        int ret;
        struct passwd *pwd;
        /* assume arguments are correct and argv[1] is the username */
        ret = pam_start("su", argv[1], &pam_conv, &pamh);
        if ( ret == PAM_SUCCESS )
           ret = pam_authenticate(pamh, 0);
        if ( ret == PAM_SUCCESS )
           ret = pam_acct_mgmt(pamh, 0);
        if ( ret == PAM_SUCCESS ) {
           if ( (pwd = getpwnam(argv[1])) != NULL )
              setuid(pwd->pw_uid);
           else {
              pam_end(pamh, PAM_AUTH_ERR);
              exit(1);
           }
        }
        pam_end(pamh, PAM_SUCCESS);
        /* return 0 on success, !0 on failure */
        return ( ret == PAM_SUCCESS ? 0 : 1 );
}
int
su_conv(int num_msg, const struct pam_message **msg,
        struct pam_response **resp, void *appdata)
{
        struct pam_message *m = *msg;
        struct pam_message *r = *resp;
        while ( num_msg-- )
        {
                switch(m->msg_style) {
                case PAM_PROMPT_ECHO_ON:
                     fprintf(stdout, "%s", m->msg);
                     r->resp = (char *)malloc(PAM_MAX_RESP_SIZE);
                     fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin);
                     m++; r++;
                     break;
                case PAM_PROMPT_ECHO_OFF:
                     r->resp = getpass(m->msg);
                     m++; r++;
                     break;
                case PAM_ERROR_MSG:
                     fprintf(stderr, "%s\n", m->msg);
                     m++; r++;
                     break;
                case PAM_TEXT_MSG:
                     fprintf(stdout, "%s\n", m->msg);
                     m++; r++;
                     break;
                default:
                        break;
                }
        }
        return PAM_SUCCESS;
}
su_conv()函数就是对话函数 - 它允许模块与用户"对话"。每一个pam_message
结构是一个信息类型,其指明模块所需数据的类型。PAM_PROMPT_ECHO_ON和
PAM_PROMPT_ECHO_OFF提示模块需要用户的信息(如密码),prompt通过模块来提供。
在PAM_PROMPT_ECHO_OFF情况中,模块通常需要一个密码,并且它会在应用程序上
不回显字符。*_MSG是用来在用户终端上显示信息的。
PAM对话的精彩之处是所有基于字符的输出可以在不改变认证模块的情况下
可以用不同的显示系统下调用函数来代替。例如:如果我们想采用基于图形的
su命令我们可以使用get_gui_passwd()来代替getpass().
注意一个真正的对话函数应该有更复杂。同样,Linux-PAM的实现提供misc_conv()
对话函数来进行命令行的交互,这个函数是一个标准对话函数所必须的。
最后,是应用程序的free()函数来释放内存。
----| 一些模块有意思的地方
现在你应该对PAM有点熟悉了,我们可以简明的讨论下定制认证的程序了,如:
我们可以方便的修改我们前面的模块以便当认证ROOT用户时,必须打第二个密码:
extern int
pam_sm_authenticate( pam_handle_t *pamh, int flgs, int c, char **v )
{
        char *user;
        char *passwd;
        struct passwd *pwd;
        int ret;
        /* ignore flags and optional arguments */
        if ( (ret = pam_get_user( ..., &user )) != PAM_SUCCESS )
           return ret;
        if ( (ret = pam_get_pass( ..., &passwd )) != PAM_SUCCESS )
           return ret;
        if ( (pwd = getpwnam(user)) != NULL ) {
           if ( !strcmp(pwd->pw_passwd, crypt(passwd)) )
              ret = PAM_SUCCESS;
           else
              ret = PAM_AUTH_ERR;
        }
        if ( !strcmp(user, "root") ) {
           pam_display_message("root user must enter secondary password");
           if ( (ret = pam_get_pass( ..., &passwd )) != PAM_SUCCESS )
              return ret;
           if ( !strcmp(get_second_root_pwd(), crypt(passwd)) )
              ret = PAM_SUCCESS;
           else
              ret = PAM_AUTH_ERR;
        }
        return ret;
}
这里我们假定这里get_second_root_pwd()函数返回一些加密的密码。当然,这个
例子有点可笑,但它演示了我们可以自由的设计我们想要的PAM模块。
----|结论
重点是PAM模块不光光是调用crypt()函数或者处理用户密码的一些类似函数,限制
你的只是你的思想。
----|参考资料
"Making Login Services Independent of Authentication Technologies".
        Samar, Vipin and Charlie Lai.
       
"The Linux-PAM System Administrator's Guide". Morgan, Andrew G.
       
"The Linux-PAM Module Writers' Guide".  Morgan, Andrew G.

"The Linux-PAM Application Developers' Guide".  Morgan, Andrew G.

Linux-PAM source code from FreeBSD 3.3 source packages.
       
一个简单的PAM配置:
BY-小许
pam可以对登陆数,进程数,内存使用数等进行限制.先要确定
/etc/pam.d/login里有以下两行:
session    required     /lib/security/pam_pwdb.so
session     required   /lib/security/pam_limits.so
然后在/etc/security/limits.conf里对用户进行限制,如:
@hacker   hard    maxlogins     3
hacker    hard    nproc         10
hacker    hard    memlock       2000
前面加@表示对这组用户进行限制

阅读(4052) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:PAM后门编译安装

给主人留下些什么吧!~~