![Linux 中的 PAM:如果远程/外部身份验证数据库返回负面响应,是否有办法跳过本地身份验证?](https://linux22.com/image/760665/Linux%20%E4%B8%AD%E7%9A%84%20PAM%EF%BC%9A%E5%A6%82%E6%9E%9C%E8%BF%9C%E7%A8%8B%2F%E5%A4%96%E9%83%A8%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%81%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%94%E5%9B%9E%E8%B4%9F%E9%9D%A2%E5%93%8D%E5%BA%94%EF%BC%8C%E6%98%AF%E5%90%A6%E6%9C%89%E5%8A%9E%E6%B3%95%E8%B7%B3%E8%BF%87%E6%9C%AC%E5%9C%B0%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%81%EF%BC%9F.png)
亲爱的系统修补者们,
一位管理员朋友问了我以下问题:
在许多网络元素和服务器上,他使用针对 TACACS+ 服务器的远程身份验证。在某些此类专有设备上,如果远程方法返回“身份验证服务器不可用”(无法访问,超时),身份验证只会询问本地身份验证数据库。如果远程身份验证服务器是可用,并返回否定响应,则有问题的盒子会按表面意义接受这一点,并且不会尝试针对本地用户数据库进行身份验证。
现在...在基于 Linux 的机器上,他想实现相同的行为。但他似乎无法做到。Linux 操作系统首先尝试远程身份验证,但在收到明确的否定响应(服务器响应“身份验证失败,这些不是有效凭据”)后,它继续尝试本地数据库。
我了解到远程 TACACS+ 身份验证由一个名为 pam_tacplus 的 PAM 模块安排。阅读有关 PAM 的一般信息后,我似乎意识到 pam_tacplus 并不是罪魁祸首。相反,观察到的行为只是 PAM 整体的工作方式。如果是这样,直接的解决方案可能必须涉及向 PAM 代码库添加可配置的全局选项,并且可能添加特定的 PAM 配置文件关键字/语法,以将行为修改为所需的方向。
欢迎就此主题发表进一步的评论:-)
答案1
...根据进一步阅读,让我提出一个答案。任何补充或更正当然都热烈欢迎。
首先我发现这个已经很久了PAM 简介。这些年来,它的敏锐才智可能并未消失。
如果我仔细查看,auth 模块的返回值是记录在官方 linux-pam 文档中。
在列表中,我可以看到一个名为 PAM_AUTHINFO_UNAVAIL 的有希望的 retval。
实际上定义在libpam/include/安全/_pam_types.h。
#define PAM_SUCCESS 0
#define PAM_AUTHINFO_UNAVAIL 9
#define _PAM_RETURN_VALUES 32
PAM_AUTHINFO_UNAVAIL 的定义似乎存在冲突libpam/include/security/_pam_compat.h
# define PAM_AUTHINFO_UNAVAIL 12
—— 或许可以忽略。
源文件libpam/pam_dispatch.c 包含一个名为 _pam_dispatch_aux() 的关键函数实际上遍历已注册身份验证模块的堆栈,并根据它们的返回值采取行动。而且,事实证明它并没有直接根据“retval”做很多事情,可能包含PAM_AUTHINFO_UNAVAIL
。它确实直接响应某些特定的“内部特殊用途”值retval
,但没有对的特殊处理PAM_AUTHINFO_UNAVAIL
。retval
不是_pam_dispatch_aux()
其自身的返回值:而是retval
只是在 for(;;) 循环内声明的局部变量,该循环遍历已注册身份验证模块的堆栈,其中块局部retval
收集特定模块的返回值。堆栈遍历循环实际上根据其他变量做出重要决定:其中一个派生,称为action
,另一个是impression
模块通过引用“返回”的结构成员。所以也许它毕竟可能受到模块自身代码的影响?
事实上,它action
是从“动作查找表”中获取的,
索引这retval
——啊哈!
action = h->actions[cached_retval];
动作表是否由模块定义?但是,如果 _PAM_ACTION_* 宏在 #defined 中定义,那么如何定义libpam/pam_private.h?
事实证明,actions[] 表是以通用方式初始化通过一个函数调用_pam_parse_conf_file()
,libpam/pam_handlers.c
它执行直接字符串比较(匹配)诸如
required
、requisite
、optional
、等关键字sufficient
。
并且,整个表目前有 32 个位置(参见上面引用的宏定义),批发初始化在解析特定模块的配置条目之前,将其更改为 _PAM_ACTION_UNDEF。然后,各个“强度关键字”将各个关键字重新映射retval
到特定的所需操作。
因此:从中获取灵感似乎是相当明显的:
} else if (!strcasecmp("required", tok)) {
D(("*PAM_F_REQUIRED*"));
actions[PAM_SUCCESS] = _PAM_ACTION_OK;
actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
_pam_set_default_control(actions, _PAM_ACTION_BAD);
} else if (!strcasecmp("sufficient", tok)) {
D(("*PAM_F_SUFFICIENT*"));
actions[PAM_SUCCESS] = _PAM_ACTION_DONE;
actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_DONE;
_pam_set_default_control(actions, _PAM_ACTION_IGNORE);
}
并添加类似
} else if (!strcasecmp("reqd_if_avail", tok)) {
D(("*PAM_F_REQD_IF_AVAIL*"));
actions[PAM_SUCCESS] = _PAM_ACTION_OK;
actions[PAM_AUTHINFO_UNAVAIL] = _PAM_ACTION_IGNORE; // !!!!!!!!!!
actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK; // ?
actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
_pam_set_default_control(actions, _PAM_ACTION_BAD); // !!!
}
显然还有 pam_tacplus帮助我们走上这条道路。
如果现有的 PAM 配置文件语法能够实现相同的效果,而无需破解源代码,那当然很好。