本文博客链接:https://zohead.com/archives/linux-pam-get-item-error-library/ 近日在将一个 RHEL6 上的服务程序移植到老的 Fedora Core 2 Linux 上时出现了一些问题,该程序的功能为预先加载几个动态库,这几个动态库中再需要根据不同的需求做 PAM 用户验证操作,程序在 RHEL6 上编译和使用都没有问题,换到 Fedora Core 2 环境之后,编译时一切正常,但在运行时出现比较奇怪的 pam_get_item 错误: pam: PAM [dlerror: /lib/security/../../lib/security/pam_env.so: undefined symbol: pam_get_item] pam: PAM [dlerror: /lib/security/../../lib/security/pam_unix.so: undefined symbol: pam_get_item] pam: PAM [dlerror: /lib/security/../../lib/security/pam_succeed_if.so: undefined symbol: pam_get_item] … 程序所加载的动态库中使用的 PAM 配置文件 /etc/pam.d/system-auth 里就用到了 pam_env.so、pam_unix.so、pam_succeed_if.so 等几个 PAM 模块。 但实际系统中还有很多服务都使用 /etc/pam.d/system-auth 这个 PAM 配置来进行用户验证,都能正常工作,因此做下简单分析。 由于此服务程序太复杂,就打定写个简单的模拟程序来进行分析。 1、动态库部分(checkpam.c): PAM 验证的实现,调用 PAM 函数进行验证,具体 PAM 函数的调用就不详细写出了。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <security/pam_appl.h> int check_pam(const char * user, const char * pass) { int ret = -1; pam_start("system-auth", user, …, …); … // 实际 PAM 代码省略,成功返回0,失败返回1 // … pam_end(…); return ret; } 和实际服务程序一致,通过 system-auth PAM 配置进行验证,编译生成动态库: cc -c checkpam.c cc -shared -fPIC -lpam -o libcheckpam.so checkpam.o ldd libcheckpam.so,查看此动态库的依赖列表: [root@linux root]# ldd libcheckpam.so linux-gate.so.1 => (0x00c67000) libpam.so.0 => /lib/libpam.so.0 (0x0025b000) libc.so.6 => /lib/tls/libc.so.6 (0x0041d000) libdl.so.2 => /lib/libdl.so.2 (0x00153000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x00876000) 2、主程序部分(pamtest.c): 加载动态库,调用 PAM 进行用户验证,比较丑陋,只需能验证,HOHO。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dlfcn.h> int main(int argc, char ** argv) { int ret = 0; void * lpso = NULL; int (* lpfunc)(const char *, const char *) = NULL; lpso = dlopen("./libcheckpam.so", RTLD_LAZY); if (lpso == NULL) { printf("Library load failed: %s.\n", dlerror()); return 1; } lpfunc = dlsym(lpso, "check_pam"); if (lpfunc == NULL) { printf("Load symbol failed: %s.\n", dlerror()); dlclose(lpso); return 2; } ret = lpfunc(argv[1], argv[2], NULL); dlclose(lpso); printf("pam ret: %d.\n", ret); return ret; } 功能很简单,dlopen 加载刚才生成的 libcheckpam.so,然后 dlsym 找到 check_pam 函数,接着调用函数进行用户验证。 编译程序: cc -o pamtest pamtest.c -ldl ldd pamtest,查看程序的依赖列表,可以看到此时已不需要 pam 库,和实际使用的服务程序的实现方式一致了: [root@linux root]# ldd pamtest linux-gate.so.1 => (0x00631000) libdl.so.2 => /lib/libdl.so.2 (0x009ac000) libc.so.6 => /lib/tls/libc.so.6 (0x0088f000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x00876000) […]