基于 PAM 的 LDAP 用户身份验证
基于 PAM 的 LDAP 身份验证要想正常的工作,还需要配置其他服务:系统命名服务和身份验证服务。
系统命名服务(NSS)需要配置为使用 LDAP 来解析诸如用户和组帐号之类的资源,即将用户的 uid 解析为用户名。由于未来用户都存储在 LDAP 目录中,因此系统需要配置成同时对 /etc/passwd 文件和 LDAP 目录中的帐号进行解析。这种功能是通过 /usr/lib/libnss_ldap.so 库提供的。
身份验证服务是实际执行 LDAP 验证用户身份的服务。Linux-PAM 提供了本地 Linux 身份验证服务。基于 PAM 的 LDAP 身份认证首先在本地的 /etc/passwd 文件检查用户帐号,如果用户在/etc/passwd 中存在,则 PAM LDAP 模块可以将身份验证重定向到 LDAP 目录上。并进行密码的校验,/lib/security/pam_ldap.so PAM 模块提供了 LDAP 身份验证功能。如果用户在/etc/passwd 中不存在,则对 LDAP 服务器进行检查,用户在 LDAP 上存在,且用户所在的组有权限登录 Linux 操作系统,则 PAM 身份认证程序自动在 Linux 操作系统上创建同名的用户,并将用户加入默认的组。以后用户只需要在 LDAP 上维护,不需要在 Linux 操作系统上维护。从而减轻了管理员的维护工作,也是用户免去了记录多个用户 ID 和密码的烦恼。
图 1. 基于 PAM 的 LDAP 身份验证流程 PAM 身份验证服务程序Linux 系统成功配置 OpenLDAP 后,为了与 PAM 集成统一管理用户,需要开发 PAM 相关的 so 文件实现用户的身份验证,在本文中 so 文件命名为 pam_ldap_union.so。当 LDAP 用户首次登陆 Linux terminal 时,首先判断用户在 Linux 操作系统中是否存在。如果用户在操作系统中存在,则直接返回 PAM_SUCCESS。如果用户不存在,则查询 OpenLDAP server。如果用户在 OpenLDAP server 存在,并且用户所在的组具有登陆 Linux 操作系统的权限,则在本地创建同名的用户,并给用户分配相应的权限。
清单 7. pam_ldap_union.so 代码示例//判断用户在 Linux 操作系统中是否存在。用户存在,直接返回 PAM_SUCCESS;否则,查询 OpenLDAP server。 int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,const char **argv) { rc = pam_get_user(pamh, &user, NULL);// the user name is in the authentication handle – pamh rc = pam_get_item(pamh, PAM_SERVICE, (const void**)&service);// get current user service. localuser = getpwnam(user); // get user from /etc/passwd if ( localuser != NULL ) { return PAM_SUCCESS; } …… } //从文件/etc/openldap/ldap.conf 获取 binddn 的 bindpw 值,作为 ldapsearch 命令的参数。 FILE *fp = fopen("/etc/openldap/ldap.conf", "r"); int rc=0; char ldapsearch[300]; //通过 ldapsearch 命令查询当前用户是否在 LDAP server 中。 sprintf(ldapsearch, "/usr/bin/ldapsearch -x -D \"%s\" -w \"%s\" cn=%s | grep \"=%s,\" 2>&1", binddn, bindpw, group, user); rc = system(ldapsearch); if(rc == 0){ printf ("pam_ldap_union: user %s found in LDAP directory", user); break; }else { printf("pam_ldap_union: user %s not found in LDAP directory ,local account will not be created", user, user); if(i == 5){ return(PAM_AUTH_ERR); } } char useradd[100]; char *homeDir = "-m"; char *primarygroup = "-g users"; char *supplgroup = "-G \'\'"; char *shell = "-s /bin/smrsh"; //如果用户在 LDAP server 中,通过 useradd 命令在 Linux 操作系统创建同名用户。 sprintf(useradd, "/usr/sbin/useradd %s %s %s %s %s >/dev/null 2>&1", homeDir, primarygroup, supplgroup, shell, user); rc = system(useradd); if ( rc != 0 ) { printf("pam_ldap_union: useradd command failed with return code %d", rc); } return PAM_SUCCESS;
PAM 身份验证配置要让 PAM 身份验证服务使用 OpenLDAP 服务器中的用户,将 pam_ldap 和 pam_ldap_union 加入到 PAM 配置文件/etc/pam.d/login 中,/etc/pam.d/login 中包含了 common-auth、common-account、common-password。将 pam_ldap 和 pam_ldap_union 按照以下方式加入到相应配置文件中。
清单 8. login 文件清单#%PAM-1.0 auth requisite pam_nologin.so auth [user_unknown=ignore success=ok ignore=ignore auth_err=die default=bad] pam_securetty.so auth include common-auth account include common-account password include common-password session required pam_loginuid.so session include common-session session required pam_lastlog.so nowtmp session optional pam_mail.so standard
清单 9. common-auth 文件清单auth required pam_tally2.so onerr=fail deny=20 unlock_time=3600 silent auth required /lib/security/pam_ldap_union.so auth sufficient /lib/security/pam_ldap.so config=/etc/openldap/ldap.conf ignore_authinfo_unavail ignore_unknown_user auth required pam_unix_auth.so try_first_pass delay #nullok set_setrpc
清单 10. common-account 文件清单:account required pam_tally2.so account required pam_unix_acct.so account sufficient /lib/security/pam_ldap.so config=/etc/openldap/ldap.conf
清单 11. common-password 文件清单Password required pam_cracklib.so type=# password sufficient /lib/security/pam_ldap.so config=/etc/openldap/ldap.conf use_authtok password required pam_unix_passwd.so use_first_pass sha512
清单 12. common-session 文件清单session required pam_limits.so session required pam_unix_session.so debug # none or trace session optional pam_umask.so