3

我正在为一个项目创建一个 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 应该如何工作的高级问题:

  1. 是否有一些管理 PAM 操作的官方标准?例如,它是否被某个地方的 POSIX 标准所覆盖?我知道有多个 PAM 实现(到目前为止我发现了四个或五个),但我不知道现有的共同点是法律上的还是事实上的,或者只是我碰巧如何配置我的系统。

  2. 在我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 交互的方式?

  1. 设置 rhost 值还是sshd来自其他地方?我已经尝试grep过 SSH 和 libpam 的源代码,但没有骰子。当调用 pam_set_item 时,我可以看到 libpam 在哪里处理主机值的设置,但实际上并没有调用 pam_set_item 来将其设置为这个或那个特定的主机。

任何数量的帮助都将不胜感激,我已经用谷歌搜索了,但我开始在我的指尖上刮擦桶底。

我有兴趣知道这一点的主要原因是,我最终不仅会得到“正确”的答案,而且主要是为了以后我不会有任何惊喜。我们可以在一些 Solaris 平台上执行此操作,但我的主要动机是基于实际不变的事物进行假设。

我还意识到我可以让客户端程序/模块将主机信息提供给库,但这可能涉及代码重写两到三次(因为 CLI 工具从 utmp 准备会话信息,从 pam_get_item 准备 PAM 模块)并可能使项目看起来比实际需要的更复杂。

4

1 回答 1

0

回答你的一些问题:

“是否有管理 PAM 操作的官方标准?”

显然,是的。维基百科关于Pluggable_Authentication_Module的条目说“PAM 被标准化为 X/Open UNIX 标准化过程的一部分,从而产生了 X/Open 单点登录 (XSSO) 标准。” 我从来没有发现这与我处理它特别相关。

“这只是因为我缺乏想象力还是必然如此?”

<magicEightBall>“集中注意力再问”</magicEightBall> (“这个”指的是哪个含糊不清——也许你能澄清一下?

“描述符 3 是否始终是身份验证客户端?”

这是应用程序的行为,而不是 PAM。

提取第一个 TCP 描述符会是建立远程客户端身份的“确定性”方式吗?

也是应用程序的一种行为。

“是 sshd 设置 rhost 值还是来自其他地方?”

设置 rhost 值的是 sshd。在 openssh 的文件 auth-pam.c 中,函数 sshpam_init(),你会发现:

sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);

一些一般注意事项:

  • 而不是关闭 TTY - 正如您稍后发现的那样 - 您可以通过 getppid() 和 /proc 遍历流程沿袭。
  • 不要信任 PAM 模块中的 system() - 它使用用户的环境,可能不是您所期望的。请改用 fork()/execlp()。
于 2013-06-28T21:23:03.290 回答