引言

https://github.com/linux-pam/linux-pam

PAM指的是Pluggable Authentication Modules,以可插拔模块的形式来帮助Linux管理系统的授权。

PAM将系统授权相关的逻辑功能做成共享链接库(/usr/lib64/security/)的形式,然后其他需要授权操作的程序只需要调用这个共享库即可。

对于管理员来说,正确的设置复杂授权等工作就变成了一个简单的管理配置文件的工作了。

/etc/pam.d/配置目录

常用的涉及到授权的命令都使用了libpam共享库,比如:

$ ldd /usr/bin/su
    libpam.so.0 => /lib64/libpam.so.0 (0x00007fb74b974000)
    libpam_misc.so.0 => /lib64/libpam_misc.so.0 (0x00007fb74b770000)
    ......

系统对常见的一些需要授权操作服务场景(比如su)默认定义好了规则:

/etc/pam.d/chfn
/etc/pam.d/chsh
/etc/pam.d/config-util
/etc/pam.d/fingerprint-auth
/etc/pam.d/login
/etc/pam.d/other
/etc/pam.d/passwd
/etc/pam.d/password-auth
/etc/pam.d/polkit-1
/etc/pam.d/postlogin
/etc/pam.d/remote
/etc/pam.d/runuser
/etc/pam.d/runuser-l
/etc/pam.d/smartcard-auth
/etc/pam.d/su
/etc/pam.d/su-l
/etc/pam.d/system-auth
/etc/pam.d/systemd-user

比如su命令对应的授权规则是:

$ cat /etc/pam.d/su

#%PAM-1.0
auth		sufficient	pam_rootok.so
# Uncomment the following line to implicitly trust users in the "wheel" group.
#auth		sufficient	pam_wheel.so trust use_uid
# Uncomment the following line to require a user to be in the "wheel" group.
#auth		required	pam_wheel.so use_uid
auth		substack	system-auth
auth		include		postlogin
account		sufficient	pam_succeed_if.so uid = 0 use_uid quiet
account		include		system-auth
password	include		system-auth
session		include		system-auth
session		include		postlogin
session		optional	pam_xauth.so

基本含义是根据规则,首先需要通过pam_rootok.so模块检验是否能够获得root身份,之后再叠加更加严格的规则。比如如果要求只有wheel用户组的用户可以使用sudo命令,则可以要求必须通过pam_wheel.so模块检验。

如果是以上服务场景之外的, 其他程序想要获取到授权,则统统归到/etc/pam.d/other规则文件里面,其内容:

$ cat /etc/pam.d/other

#%PAM-1.0
auth     required       pam_deny.so
account  required       pam_deny.so
password required       pam_deny.so
session  required       pam_deny.so

即系统默认自带的授权场景之外的其他授权行为一律拒绝。

授权规则语法

一般Unix系统里面是使用文件/etc/pam.conf来配置规则,其语法是:

[service name] module-type control-flag module-path [module-arguments]

但在Linux里面常见的做法是将每个service name以文件的形式单独存放到目录/etc/pam.d/中,并且要求文件名必须是小写:

module-type control-flag module-path [module-arguments]

其中module-type参数是auth,account,session,password.

其中control-flag的含义如下:

Flag标志 失败后是否停止 成功后是否停止 备注
binding No Yes
include - - 包含其他服务规则进来
optional No No 认证失败也忽略,除非是唯一的规则
required No No 任何规则失败导致整体授权失败,但会全部执行完再返回错误
requisite Yes No 一旦规则失败立即返回错误,不执行后面其他规则验证
sufficient No Yes 只要通过当前模块认证就立马返回成功信息,即使前面有错误认证

自定义授权服务与规则

除非特殊情况,一般不应该去修改系统自带的授权服务规则文件,但可以自定义新的授权服务规则。

比如这里创建一个叫check的服务配置文件/etc/pam.d/check:

auth       required     pam_unix_auth.so
account    required     pam_unix_acct.so
session    required     pam_limits.so    conf=/etc/security/limits.conf

现有一个程序调用这个授权服务:

// https://github.com/linux-pam/linux-pam/blob/master/examples/check_user.c

#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <stdio.h>

static struct pam_conv conv = {
    misc_conv,
    NULL
};

int main(int argc, char *argv[])
{
    pam_handle_t *pamh=NULL;
    int retval;
    const char *user="nobody";

    if(argc == 2) {
    user = argv[1];
    }

    if(argc > 2) {
    fprintf(stderr, "Usage: check_user [username]\n");
    exit(1);
    }

    // 调用check授权服务
    retval = pam_start("check", user, &conv, &pamh); 

    if (retval == PAM_SUCCESS)
        retval = pam_authenticate(pamh, 0);    /* is user really user? */

    if (retval == PAM_SUCCESS)
        retval = pam_acct_mgmt(pamh, 0);       /* permitted access? */

    /* This is where we have been authorized or not. */

    if (retval == PAM_SUCCESS) {
    fprintf(stdout, "Authenticated\n");
    } else {
    fprintf(stdout, "Not Authenticated\n");
    }

    if (pam_end(pamh,retval) != PAM_SUCCESS) {     /* close Linux-PAM */
    pamh = NULL;
    fprintf(stderr, "check_user: failed to release authenticator\n");
    exit(1);
    }

    return ( retval == PAM_SUCCESS ? 0:1 );       /* indicate success */
}

这样新开发的程序就可以使用自定义的授权服务规则了:

$ yum install pam-devel

$ gcc main.cpp  -lpam -lpam_misc -o check_user

$ ./check_user
Password:
Not Authenticated

$ ./check_user test
Password:
Authenticated

$ ./check_user test
Password:
Not Authenticated

pam_limits管理会话资源分配

日常接触较多的是会话资源管理,比如:

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 7475
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1048576
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) unlimited
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

这是系统当前会话的资源限制情况,它其实检查这些资源限制是通过pam_limits模块进行的,这个模块默认是读取配置文件/etc/security/limits.conf,还会读取/etc/security/limits.d/目录下的配置文件。

如果是临时修改会话资源限制,则使用ulimit命令:

$ ulimit -s
10240

$ ulimit -s 8192

$ ulimit -s
8192

$ ulimit -s unlimited

$ ulimit -s
unlimited

如果是长期生效则修改配置文件/etc/security/limits.conf:

#<domain>        <type>  <item>        <value>
testUser         hard    maxlogins     1      # 该用户只可以有1个登录会话
@testGrp         hard    maxlogins     1      # 该用户组用户只可以有1个登录会话
*                hard    maxsyslogins  10     # 系统最多10个会话同时在线
testUser         hard    stack         10240  # 堆栈空间
testUser         soft    core          2      # 最多使用2个核CPU资源
testUser         hard    nofile        1024   # 最多打开1024个文件

总结

PAM是方便Linux管理授权和资源分配的机制,而其中pamd_limits是常用的管理会话资源的模块。