身份验证现代化:使用 GNU Guile 编写 Linux-PAM 模块

三十多年来,Linux-PAM (Pluggable Authentication Modules) 一直是 Linux 系统身份验证的基石。其设计在当时具有革命性,允许像 sulogin 这样的安全相关应用程序遵循一致的策略,而无需在策略更改时重新编译。然而,对共享 C 对象的依赖往往使得编写和维护自定义模块的过程变得繁琐且不透明。

Guile-PAM 作为一种现代替代方案出现,它使系统管理员能够使用 GNU Guile(Scheme 的一种方言)来编写、配置和维护 PAM 模块。通过弥合低级 PAM API 与高级解释型语言之间的鸿沟,Guile-PAM 将身份验证逻辑从静态编译的二进制文件转变为灵活、可读的脚本。

Guile-PAM 的架构

Guile-PAM 的核心是通过一个特别构建的共享对象 pam_guile.so 进行操作。该对象充当网关,调用 GNU Guile 脚本来处理身份验证任务。这种架构允许三个不同层级的集成:

  1. 特定任务模块:使用 pam_guile.so 作为标准的 Linux-PAM 共享对象,在 Guile 中执行单个特定任务。
  2. 服务替换:通过将 pam_guile.so 作为唯一的共享对象并在此 Guile 中处理所有逻辑,来替换整个 PAM 方案(即特定服务)。
  3. 系统级编排:将 /etc/pam.d 中的所有服务替换为 pam_guile.so,从而有效地将方案选择和逻辑都移入 Guile 环境。

理解 "Pamdas"

在 Guile-PAM 中,逻辑的基本单位是 Pamda(PAM 和 Lambda 的合成词)。一个 Pamda 是一个具有以下签名的匿名过程:

(lambda (action handle flags options) ...)
  • Action:六个符号之一(例如 pam_sm_authenticatepam_sm_open_session),代表 PAM 入口点。
  • Handle:一个不透明变量,提供对内部 PAM 数据的访问,例如用户名和会话对话函数。
  • Flags:一个整数位掩码。
  • Options:从服务定义中传递的字符串列表。

重新实现 PAM 栈

Guile-PAM 最强大的功能之一是在 Scheme 中完全重新实现了 Linux-PAM 栈。开发者不再依赖 Linux-PAM 的静态配置文件,而是可以通过编程方式定义“门”(gates)和“计划”(plans)。

门与计划

gate 过程封装了一个 pamda 并为其分配一个计划(例如 requiredoptionalsufficientrequisite)。随后,stack 过程会按顺序评估这些门。

对于那些从传统设置迁移的用户,Guile-PAM 提供了 control-string->plan,它可以将标准的 Linux-PAM 控制字符串(例如 success=ok ignore=ignore default=bad)直接转换为 Guile-PAM 计划。

混合栈

Guile-PAM 并不要求完全放弃现有的基于 C 的模块。通过使用 call-shared-object,Guile-PAM 栈可以调用传统的 .so 文件。为了防止稳定性问题——因为许多传统模块在被调用执行它们未实现的动作时会返回 PAM_SERVICE_ERR——Guile-PAM 允许开发者使用 #:implements 关键字来精确指定共享对象实现了哪些动作。

高级用例:超越简单的身份验证

由于 Guile-PAM 是具有系统访问权限的解释型语言,它可以实现标准 PAM 模块难以实现或无法实现的功能。

示例:用户级挂载

A 驱动 Guile-PAM 的主要动机是能够以实际用户而非 root 身份挂载卷。虽然存在 pam_mount.so,但它通常以 root 身份挂载,这在处理 FUSE 文件夹或 kerberized NFSv4 时会失败。

Guile-PAM 允许一种工作流:在 pam_sm_authenticate 期间接收到的令牌被存储起来,然后在 pam_sm_open_session 期间使用,将秘密信息通过管道传递给像 gocryptfs 这样的用户空间工具,从而在登录过程中以用户身份挂载主目录。

安全与性能考量

将身份验证逻辑移至解释型语言会引入特定的考量因素:

安全性

  • 可读性:作者认为 Scheme 代码通常比 C 更容易审计,从而增加了同行评审的可能性。
  • 注入风险:为了防止攻击,作者警告开发者不要在未知输入上使用 Guile 的 read 过程。
  • 环境隔离pam_guile.so 隔离了诸如 GUILE_LOAD_PATH 之类的环境变量,以防止未经授权的模块替换。

性能

实际测试表明,Guile-PAM 的运行速度处于合理水平。有趣的是,作者指出,在身份验证中,极速并不总是目标,因为为了挫败暴力破解攻击,通常会内置一些故意延迟。

实现注意事项

用户应当意识到 Guile-PAM 目前处于 alpha 阶段。与成熟的 Linux-PAM 相比,存在关键的行为差异:

  • 动作分组:Linux-PAM 将某些动作(如 authsetcred)分组,并且如果第一个动作返回 PAM_IGNORE,则可能会跳过第二个。Guile-PAM 的 (pam stack) 实现会为每个动作评估每一个模块。
  • 跳过计数:在具有显式跳过计数的传统指令集中,未解锁的 Guile-PAM 栈始终会获取结果,而 Linux-PAM 的副作用则因动作而异。

结论

Guile-PAM 代表了向更透明、更灵活的系统管理模式的转变。通过将身份验证视为一个可编程的逻辑问题,而非二进制块的配置,它为新颖的分支策略以及与其他自由软件系统(如 OpenBSD 脚本)的更深层次集成打开了大门。对于使用 GNU Guix 的用户来说,这种集成甚至更加无缝,允许整个身份验证栈都在系统配置中定义。

Sources