我正在为一个项目创建一个 PAM 模块。PAM 模块将使用一个库,该库将被一些命令行实用程序重用(而不是每次都重写所有内容)。在这个库中,我想让它解释根据远程主机的子网成员身份区分和/或记录的策略。据我所知,这个值可能来自身份验证应用程序,但我不知道。由于共享对象无法从 libpam 访问 pamh 结构,我不能只做一个 pam_get_item(就像我可以从 PAM 模块本身一样)所以我不得不求助于其他方法。
我想出的最佳解决方案是让共享对象查找已连接的 TTY,如果存在,请转到 utmp 并找到与该 TTY 关联的登录过程,然后从那里提取 IP 地址。如果没有 TTY,则假设它是网络用户的初始登录。然后该库遍历套接字(当您执行 a 时,我基本上将其定义为目标文件名中带有单词“socket”的任何符号链接ls -l /proc/<pid>/fd
)并使用套接字 inode 编号与/proc/net/tcp
并提取与该 inode 号关联的远程 IP 地址。如果它在那里没有找到一个 inode,那么它假定它是 Unix 域或 tcp6(IPv6 支持即将到来,并且在不久的将来并不是非常重要)。如果它仍然无法找到它,假设某个守护进程调用了一个链接它的应用程序并将其解释为这样(最终可能会做一些事情,如果它是值得的,但现在它只是一个很大的 NOOP 如果前两个不不返回任何东西。
它似乎有效,但我有一些关于 PAM 应该如何工作的高级问题:
是否有一些管理 PAM 操作的官方标准?例如,它是否被某个地方的 POSIX 标准所覆盖?我知道有多个 PAM 实现(到目前为止我发现了四个或五个),但我不知道现有的共同点是法律上的还是事实上的,或者只是我碰巧如何配置我的系统。
在我
ls -l /proc/<pid>/fd > /lsOutput
从模块本身(通过system()
)做了一个之后:
[root@hypervisor pam]# cat /lsOutput 总计 0
lrwx-----。1 根 64 Jun 15 15:09 0 -> /dev/null
lrwx-----。1 根 64 Jun 15 15:09 1 -> /dev/null
lrwx-----。1 根 64 Jun 15 15:09 2 -> /dev/null
lr-x-----。1 根 64 Jun 15 15:09 3 -> 套接字:[426180]
[root@hypervisor pam]#
ls
并在用户登录后发布手册:
[root@hypervisor pam]# ls -l /proc/18261/fd
total 0
lrwx------. 1 root root 64 Jun 15 15:15 0 -> /dev/null
lrwx------. 1 root root 64 Jun 15 15:15 1 -> /dev/null
lrwx------. 1 root root 64 Jun 15 15:15 11 -> /dev/ptmx
lrwx------. 1 root root 64 Jun 15 15:15 12 -> /dev/ptmx
lrwx------. 1 root root 64 Jun 15 15:15 13 -> socket:[426780]
lrwx------. 1 root root 64 Jun 15 15:15 14 -> socket:[426829]
lrwx------. 1 root root 64 Jun 15 15:15 2 -> /dev/null
lrwx------. 1 root root 64 Jun 15 15:15 3 -> socket:[426180]
lrwx------. 1 root root 64 Jun 15 15:15 4 -> socket:[426322]
lr-x------. 1 root root 64 Jun 15 15:15 5 -> pipe:[426336]
l-wx------. 1 root root 64 Jun 15 15:15 6 -> pipe:[426336]
lrwx------. 1 root root 64 Jun 15 15:15 7 -> socket:[426348]
lrwx------. 1 root root 64 Jun 15 15:15 8 -> socket:[426349]
lrwx------. 1 root root 64 Jun 15 15:15 9 -> /dev/ptmx
[root@hypervisor pam]#
所以基本上,似乎只有在会话模块完成后才打开 TTY 和任何其他套接字(我的临时测试模块的会话处理是sshd
服务堆栈中的最后一个)。我一直无法做到这一点(或者甚至想到连接客户端不会是描述符 3 处的 TCP 套接字的时候)。
这仅仅是因为我缺乏想象力还是必然如此?我倾向于后者,因为与客户沟通似乎是做任何其他有用的事情的先决条件。我不确定,所以我觉得我应该问问别人。描述符 3 是否始终是身份验证客户端(我的 .so 仅假设它是编号最小的 TCP 套接字,并且仅在没有 TTY 的情况下,但似乎3
应该始终是连接客户端的描述符)。提取第一个 TCP 描述符会是建立远程客户端身份的“确定性”方式吗?还是没有规定的方式应该发挥作用,这就是我的系统配置方式或 SSH 选择与 PAM 交互的方式?
- 是设置 rhost 值还是
sshd
来自其他地方?我已经尝试grep
过 SSH 和 libpam 的源代码,但没有骰子。当调用 pam_set_item 时,我可以看到 libpam 在哪里处理主机值的设置,但实际上并没有调用 pam_set_item 来将其设置为这个或那个特定的主机。
任何数量的帮助都将不胜感激,我已经用谷歌搜索了,但我开始在我的指尖上刮擦桶底。
我有兴趣知道这一点的主要原因是,我最终不仅会得到“正确”的答案,而且主要是为了以后我不会有任何惊喜。我们可以在一些 Solaris 平台上执行此操作,但我的主要动机是基于实际不变的事物进行假设。
我还意识到我可以让客户端程序/模块将主机信息提供给库,但这可能涉及代码重写两到三次(因为 CLI 工具从 utmp 准备会话信息,从 pam_get_item 准备 PAM 模块)并可能使项目看起来比实际需要的更复杂。