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

news2025/1/13 5:59:14

接前一篇文章: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函数的其余部分将在后续文章中继续讲解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1127785.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

方案聚焦:高可用的F5分布式云DNS负载均衡

DNS是实现互联网的主要技术之一。它也是网络基础设施的重要组成部分,DNS管理一个分布式和冗余的架构,确保高可用性和高质量的用户响应时间,因此拥有一个可用的、智能的、安全和可扩展的DNS基础设施是至关重要的。然而DNS没有真正的能力来分配…

如何有效取代FTP来帮助企业快速传输大文件

在互联网的发展历史上,FTP是一种具有里程碑意义的协议,它最早出现在1971年,是实现网络上文件传输的基础。FTP的优点是简单、稳定、兼容性强,可以在不同的操作系统和平台之间进行文件交换。然而,时代在进步,…

系统升级数量超微软预期,Win10/11盗版激活被封杀

声明:本文提供的命令、工具来自第三方网站,仅供学习交流使用,下载后24小时内删除,一切非法使用责任由使用者自行承担。 上月底 Win11 迎来了 Moment 4 功能更新,任务栏取消合并居然真的回归了。 巨硬终于妥协&#x…

软件开发行业的乱象——低价引流中途收费?299?399?

在当今的软件开发行业,存在着许多乱象。这些乱象不仅影响了软件开发的效率和效果,也给整个行业带来了许多负面影响。其中,价格方面是软件开发行业乱象的一个重要方面。 首先,从价格方面来看,软件开发行业的价格标准非常…

【均值漂移】mean-shift算法详解

Mean-shift算法是一种非参数密度估计算法,主要用于图像分割、目标跟踪和聚类等领域。其基本原理是以某个点为中心,计算该点周围所有点的密度,并将中心点移动到密度最大的位置,不断迭代,直到中心点不再移动或满足停止条…

常用智能优化算法改进策略---飞行游走篇(五种策略)可用于改进所有智能算法,让小白也会改进智能算法。...

本期文章将讲述常用智能优化算法改进策略---飞行游走篇,一共包含五种常见的改进策略: ①莱维飞行,②随机游走,③螺旋飞行,④高斯随机游走,⑤三角形游走 五种策略可以方便移植到其他任何智能算法的改进中&am…

开发常用的 Linux 命令知识积累

查看硬盘的使用情况df -h单元为根据大小适当显示,-m单位为M Linux中查找文件夹的命令是find命令。 全盘搜索,也可以指定目录搜索。find 搜索目录 -name 目标名字,find / -name file liunx自己总结常见命令 执行命令 含义 cd ~ 切换到登录…

了解docker

了解docker docker版本演进docker架构docker生态docker安装 docker版本演进 lxc: lxc 是最早的 linux 容器技术,早期版本的 docker 直接使用 lxc 来实现容器的底层功能。虽然使用者相对较少,但 lxc 项目仍在持续开发演进中libcontainer&#…

Flutter饱受争议的7个缺点,大家怎么看?

Flutter是一款由Google推出的跨平台移动应用开发框架,近年来备受关注。尽管Flutter在某些方面表现出色,但仍然有一些人对它的发展前景表示怀疑。近期一些文章针对Flutter的发展提出了不少质疑和批评,称其难以成为移动应用开发的“顶流明星”&…

Hacker 资讯 | 10 月下旬区块链黑客松活动汇总

「TinTin Hacker 快讯」是 TinTinLand 建立的一个资讯专栏,汇集近期线上线下的黑客松及 Grant,旨在帮助开发者和区块链爱好者获取最新的黑客松资讯,鼓励他们了解并根据自身情况参与不同的黑客松,更好地建设 Web3 生态。 ETHMiami …

1.初识MySQL

初识 MySQL 1.服务器处理客户端请求2.常用存储引擎3.关于存储引擎的一些操作3.1 查看当前服务器程序支持的存储引擎3.2 设置表的存储引擎3.2.1 创建表时指定存储引擎3.2.2 修改表的存储引擎 4.总结 MySQL 默认采用 TCP/IP 的方式来处理客户端与服务器连接过程。 1.服务器处理客…

2023高频前端面试题-vue

1. 什么是 M V VM Model-View-ViewModel 模式 Model 层: 数据模型层 通过 Ajax、fetch 等 API 完成客户端和服务端业务模型的同步。 View 层: 视图层 作为视图模板存在,其实 View 就是⼀个动态模板。 ViewModel 层: 视图模型层 负责暴露数据给 View 层&…

解决oracle12c安装失败【INS-30131】执行安装程序验证所需要的初始设置失败问题

最近看到很多公司都要求会使用Oracle数据库,所以我就在网上找了Oracle教程,打算学习一下,可人生就是喜欢捉弄我,Oracle安装竟然都出了问题,别提学习了,可真让我很难受,那么安装出现了什么问题呢? 看下图: 原因 - 无法访问临时位置。 操作 - 请确保当前用户具有访问临时位置所…

muduo源码学习base——Atomic(原子操作与原子整数)

Atomic(原子操作与原子整数) 前置知识AtomicIntegerTget()getAndAdd()getAndSet() 关于原子操作实现无锁队列(lock-free-queue) 前置知识 happens-before: 用来描述两个操作的内存可见性 如果操作 X happens-before 操作 Y,那么 X 的结果对于…

极智AI | 有趣的羊驼系列大模型

欢迎关注我的公众号 [极智视界],获取我的更多经验分享 大家好,我是极智视界,本文来介绍一下 有趣的羊驼系列大模型。 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码下载,链接:https://t.zsxq.com/0aiNxERDq "羊驼模型" 在大模型的介绍中应…

CodeWhisperer proxy代理连不上(解决)

报错: 2023-10-24 14:15:50 [INFO]: selected AWS ID sign in 2023-10-24 14:15:50 [ERROR]: API response (oidc.us-east-1.amazonaws.com /device_authorization): {} 2023-10-24 14:15:50 [ERROR]: webviewId"authWebview": Error: Webview error->…

【外汇天眼】很多交易高手都容易忽视的问题:“路径依赖”!

一、“路径依赖”与“均值回归” 在过去五到十年内,如果你一直通过采用某一策略取得持续的超额收益,那么你很难认识到这种策略可能出现错误。 即使经历了一两年的失效,大多数人都会继续相信并采用这一策略。这受到沉没成本、个人声誉以及多种…

Android Apk一键打包上传至蒲公英平台的gradle脚本

一、背景 项目中每次手动打包后,生成的测试包,都需要手动打开蒲公英平台的网址,登录账号,手动上传apk。之前写过一键上传至fir平台的脚本,想着这次可以搞一下一键打包上传至蒲公英的gradle脚本,提高下工作…

Linux下QT打开文件选择对话框时,程序报错退出

系统:Ubuntu QString fileName QFileDialog::getOpenFileName(this, "open", "./", "document Files (*.pdf)"); 调用该语句弹出文件对话框时,程序崩溃退出 错误提示: (Widget:5272): Gtk-WARNING **: 14…

73 应急响应-WEB分析phpjavaweb自动化工具

目录 应急响应:必备知识点:准备工作:有明确信息网站被入侵:无明确信息网站被入侵:常见分析方法: 演示案例:WindowsIISSql-日志,搜索LinuxBT_Nginxtp5-日志,后门360星图日志自动分析工…