接前一篇文章:PAM从入门到精通(十九)
本文参考:
《The Linux-PAM Application Developers' Guide》
先再来重温一下PAM系统架构:
更加形象的形式:
七、PAM-API各函数源码详解
前边的文章讲解了各PAM-API函数以及总体流程,但是也只是从接口层面介绍的,并没有深入到代码层面。从本篇文章开始,将对于各个接口函数从源码级进行讲解,以使大家不但知其然,还要知其所以然。
1. pam_start函数
老规矩,先从pam_start函数开始。先再来看一下pam_start()的说明。
概述:
PAM事务初始化。
函数声明:
#include <security/pam_appl.h>
int pam_start ( service_name , user , pam_conversation , pamh );
const char * service_name ;
const char * user ;
const struct pam_conv * pam_conversation ;
pam_handle_t ** pamh ;
其余函数相关细节说明随着源码一起讲解。
pam_start函数在Linux-PAM-1.5.2源码根目录的libpam/pam_start.c中,代码如下:
int pam_start (const char *service_name,const char *user,const struct pam_conv *pam_conversation,pam_handle_t **pamh)
{return _pam_start_internal(service_name, user, pam_conversation,NULL, pamh);
}
由代码可见,pam_start()只是一层简单的封装,实际的工作完全交给了_pam_start_internal函数。该函数在同文件(libpam/pam_start.c)中,代码如下:
static int _pam_start_internal (const char *service_name,const char *user,const struct pam_conv *pam_conversation,const char *confdir,pam_handle_t **pamh)
{D(("called pam_start: [%s] [%s] [%p] [%p]",service_name, user, pam_conversation, pamh));if (pamh == NULL) {pam_syslog(NULL, LOG_CRIT,"pam_start: invalid argument: pamh == NULL");return (PAM_SYSTEM_ERR);}if (service_name == NULL) {pam_syslog(NULL, LOG_CRIT,"pam_start: invalid argument: service == NULL");return (PAM_SYSTEM_ERR);}if (pam_conversation == NULL) {pam_syslog(NULL, LOG_CRIT,"pam_start: invalid argument: conv == NULL");return (PAM_SYSTEM_ERR);}if ((*pamh = calloc(1, sizeof(**pamh))) == NULL) {pam_syslog(NULL, LOG_CRIT, "pam_start: calloc failed for *pamh");return (PAM_BUF_ERR);}/* All service names should be files below /etc/pam.d and nothingelse. Forbid paths. */if (strrchr(service_name, '/') != NULL)service_name = strrchr(service_name, '/') + 1;/* Mark the caller as the application - permission to do certainthings is limited to a module or an application */__PAM_TO_APP(*pamh);if (((*pamh)->service_name = _pam_strdup(service_name)) == NULL) {pam_syslog(*pamh, LOG_CRIT,"pam_start: _pam_strdup failed for service name");_pam_drop(*pamh);return (PAM_BUF_ERR);} else {char *tmp;for (tmp=(*pamh)->service_name; *tmp; ++tmp)*tmp = tolower(*tmp); /* require lower case */}if (user) {if (((*pamh)->user = _pam_strdup(user)) == NULL) {pam_syslog(*pamh, LOG_CRIT,"pam_start: _pam_strdup failed for user");_pam_drop((*pamh)->service_name);_pam_drop(*pamh);return (PAM_BUF_ERR);}} else(*pamh)->user = NULL;if (confdir) {if (((*pamh)->confdir = _pam_strdup(confdir)) == NULL) {pam_syslog(*pamh, LOG_CRIT,"pam_start: _pam_strdup failed for confdir");_pam_drop((*pamh)->service_name);_pam_drop((*pamh)->user);_pam_drop(*pamh);return (PAM_BUF_ERR);}} else(*pamh)->confdir = NULL;(*pamh)->tty = NULL;(*pamh)->prompt = NULL; /* prompt for pam_get_user() */(*pamh)->ruser = NULL;(*pamh)->rhost = NULL;(*pamh)->authtok = NULL;(*pamh)->oldauthtok = NULL;(*pamh)->fail_delay.delay_fn_ptr = NULL;(*pamh)->former.choice = PAM_NOT_STACKED;(*pamh)->former.substates = NULL;
#ifdef HAVE_LIBAUDIT(*pamh)->audit_state = 0;
#endif(*pamh)->xdisplay = NULL;(*pamh)->authtok_type = NULL;(*pamh)->authtok_verified = 0;memset (&((*pamh)->xauth), 0, sizeof ((*pamh)->xauth));if (((*pamh)->pam_conversation = (struct pam_conv *)malloc(sizeof(struct pam_conv))) == NULL) {pam_syslog(*pamh, LOG_CRIT, "pam_start: malloc failed for pam_conv");_pam_drop((*pamh)->service_name);_pam_drop((*pamh)->user);_pam_drop((*pamh)->confdir);_pam_drop(*pamh);return (PAM_BUF_ERR);} else {memcpy((*pamh)->pam_conversation, pam_conversation,sizeof(struct pam_conv));}(*pamh)->data = NULL;if ( _pam_make_env(*pamh) != PAM_SUCCESS ) {pam_syslog(*pamh,LOG_ERR,"pam_start: failed to initialize environment");_pam_drop((*pamh)->pam_conversation);_pam_drop((*pamh)->service_name);_pam_drop((*pamh)->user);_pam_drop((*pamh)->confdir);_pam_drop(*pamh);return PAM_ABORT;}_pam_reset_timer(*pamh); /* initialize timer support */_pam_start_handlers(*pamh); /* cannot fail *//* According to the SunOS man pages, loading modules and resolving* symbols happens on the first call from the application. */if ( _pam_init_handlers(*pamh) != PAM_SUCCESS ) {pam_syslog(*pamh, LOG_ERR, "pam_start: failed to initialize handlers");_pam_drop_env(*pamh); /* purge the environment */_pam_drop((*pamh)->pam_conversation);_pam_drop((*pamh)->service_name);_pam_drop((*pamh)->user);_pam_drop((*pamh)->confdir);_pam_drop(*pamh);return PAM_ABORT;}D(("exiting pam_start successfully"));return PAM_SUCCESS;
}
_pam_start_internal函数一开始先给出调试信息,并且进行参数检查。代码片段如下:
D(("called pam_start: [%s] [%s] [%p] [%p]",service_name, user, pam_conversation, pamh));if (pamh == NULL) {pam_syslog(NULL, LOG_CRIT,"pam_start: invalid argument: pamh == NULL");return (PAM_SYSTEM_ERR);}if (service_name == NULL) {pam_syslog(NULL, LOG_CRIT,"pam_start: invalid argument: service == NULL");return (PAM_SYSTEM_ERR);}if (pam_conversation == NULL) {pam_syslog(NULL, LOG_CRIT,"pam_start: invalid argument: conv == NULL");return (PAM_SYSTEM_ERR);}
service_name是由pam_start函数传下来的参数,其作用是:指定要应用的服务的名称,并将作为PAM_SEVICE项存储在新上下文中。服务的策略将从文件/etc/pam.d/service_name中读取,如果该文件不存在,则从/etc/pam.conf中读取。如:passwd(/etc/pam.d/passwd)、useradd(/etc/pam.d/useradd)等。
pamh也是由pam_start函数传下来的参数,其内容是一个句柄,它包含对PAM函数的连续调用的PAM上下文。在错误情况下,pamh的内容未定义。pam_handle_t是一个盲结构,应用程序不应试图直接探测其信息。而是应该通过PAM库提供的函数pam_set_item和pam_get_item。PAM句柄不能同时用于多个身份验证,只要以前没有对其调用pam_end函数。
之前一直没有给出pamh的类型pam_handle_t的定义,在这里必须要弄清楚了。其定义在libpam/pam_private.h中,代码如下:
struct pam_handle {char *authtok;unsigned caller_is;struct pam_conv *pam_conversation;char *oldauthtok;char *prompt; /* for use by pam_get_user() */char *service_name;char *user;char *rhost;char *ruser;char *tty;char *xdisplay;char *authtok_type; /* PAM_AUTHTOK_TYPE */struct pam_data *data;struct pam_environ *env; /* structure to maintain environment list */struct _pam_fail_delay fail_delay; /* helper function for easy delays */struct pam_xauth_data xauth; /* auth info for X display */struct service handlers;struct _pam_former_state former; /* library state - support forevent driven applications */const char *mod_name; /* Name of the module currently executed */int mod_argc; /* Number of module arguments */char **mod_argv; /* module arguments */int choice; /* Which function we call from the module */#ifdef HAVE_LIBAUDITint audit_state; /* keep track of reported audit messages */
#endifint authtok_verified;char *confdir;
};
libpam/include/security/_pam_types.h中:
/* This is a blind structure; users aren't allowed to see inside a* pam_handle_t, so we don't define struct pam_handle here. This is* defined in a file private to the PAM library. (i.e., it's private* to PAM service modules, too!) */typedef struct pam_handle pam_handle_t;
pam_conversation也是由pam_start函数传下来的参数,其指向描述要使用的会话函数的结构pam_conv。应用程序必须为加载的模块与应用程序之间的直接通信提供此功能。
struct pam_conv的定义在libpam/include/security/_pam_types.h中,代码如下:
/* The actual conversation structure itself */struct pam_conv {int (*conv)(int num_msg, const struct pam_message **msg,struct pam_response **resp, void *appdata_ptr);void *appdata_ptr;
};
做完第一步参数检查的工作后,下边正式开始进行实际功能实现。欲知后事如何,且看下回分解。