PAM从入门到精通(二十三)

本文详细解析PAM系统中的pam_start函数,涉及参数验证、内存分配和环境初始化等,深入到源代码层次,帮助理解PAM工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接前一篇文章:PAM从入门到精通(二十二)

本文参考:

《The Linux-PAM Application Developers' Guide》

先再来重温一下PAM系统架构:

更加形象的形式:

七、PAM-API各函数源码详解

前边的文章讲解了各PAM-API函数以及总体流程,但是也只是从接口层面介绍的,并没有深入到代码层面。从本篇文章开始,将对于各个接口函数从源码级进行讲解,以使大家不但知其然,还要知其所以然。

1. pam_start函数

上回讲到_pam_start_internal函数的第三部分,本文继续往下进行讲解。为了便于理解,再次贴出_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 nothing
       else. Forbid paths. */
    if (strrchr(service_name, '/') != NULL)
	service_name = strrchr(service_name, '/') + 1;
 
    /* Mark the caller as the application - permission to do certain
       things 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;
}

接下来来到以下代码片段:

    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;

user也是由pam_start()传过来的参数。一般调用pam_start函数的时候,传入的参数都是这样:

    pam_handle_t *pamh = NULL;
   
    /* 初始化,并提供一个回调函数 */
    if ((pam_start("login", user_name, &conv, &pamh)) != PAM_SUCCESS)
        exit(1);

因此,此处的user就是上边代码中的user_name,即用户名,也就是/home/xxx的xxx。

如果user传入的是NULL,则直接将(*pamh)->user赋值为NULL;如果user传入的不是NULL,比如上边的user_name,则处理与上一回中讲解的对于service_name的处理相似。在此就不赘述了。

接下来来到以下代码段:

    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;

由于我们是从pam_start()调用下来的,而pam_start函数(代码如下)在调用_pam_start_internal函数时,传入const char *confdir参数的值固定为NULL,因此此处直接走else分支,将(*pamh)->confdir设置为NULL。

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);
}

至于何时传入传入const char *confdir参数的值不是NULL,那得是同文件(libpam/pam_start.c)中的pam_start_confdir函数,代码如下:

int pam_start_confdir (
    const char *service_name,
    const char *user,
    const struct pam_conv *pam_conversation,
    const char *confdir,
    pam_handle_t **pamh)
{
    return _pam_start_internal(service_name, user, pam_conversation,
			       confdir, pamh);
}

在此种情况下,处理方式也和前边的service_name和user相似。

_pam_start_internal函数的其余部分将在后续文章中继续讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝天居士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值