摘要

  • RFC 1509 把 gss_cred_id_t 定义为调用者看不见内部结构的原子引用,实际凭据保留在 GSS-API 或底层机制中;句柄数值本身不含安全相关信息。
  • 规范明确允许相同数值在不同调用者那里指向不同凭据。句柄的意义必须连同进程或 UID 范围、实现状态、机制、主体、用途和有效期一起重建。
  • 真正跨网络传递的是机制生成的认证令牌,不是本地句柄。认证完成后,应用授权、消息交付和业务结果仍需各自回执。

被复制的只是数值

设想进程甲持有一个 gss_cred_id_t,其机器表示看起来像整数 7。进程乙也拿到整数 7。两者调用同一个 GSS-API 函数时,是否必然使用同一份凭据?

RFC 1509 的答案是否定的。这个 1993 年 9 月发布的 C 语言绑定允许实现把句柄做成指针或算术值,却明确说句柄是相对于调用者解释的。完全相同的原子值交给不同调用者,可以指向不同凭据。

因此,复制数值没有完成对象迁移。另一个进程可能没有对应表项,可能在自己的表项中找到另一主体的凭据,也可能依据不同机制解释引用。只有当本地实现认可调用者范围并能从自己的状态中解析该值时,句柄才成为可用引用。

这种设计不是接口缺陷。它是抽象的边界:应用获得一种调用接口,而不是获得底层凭据元素。

凭据留在服务内部

RFC 1509 信息页 记录作者 John Wray、最初的 Proposed Standard 状态以及后来被 RFC 2744 取代的关系。绑定描述四个阶段:取得凭据;与对端建立共同安全上下文;在消息上使用完整性或保密服务;通信会话结束时删除上下文。

这里至少有七种不同对象:调用进程、本地凭据句柄、句柄背后的凭据材料、凭据可声明的主体、与对端共同建立的上下文、承载协议交换的连接,以及应用最终允许或拒绝的操作。

通信会话还可以跨越多个连接,同一关联也能同时存在多个安全上下文。连接不是上下文,句柄不是上下文内部状态,认证主体也不是应用权限。

如果日志只记录“句柄 7”,它没有保存哪一个调用者、哪一代本地状态、哪一种机制或哪一个主体。它记住了语法,却丢失了权威来源。

句柄范围是一条隐含关系

RFC 1509 要求实现说明句柄和凭据的作用范围。句柄可能只对取得它的进程有效,也可能传给子进程,或允许共享某种本地身份(例如同一 UID)的进程使用。最后一个可达句柄消失后,凭据才可能被删除。

为什么标准没有选择唯一答案?RFC 1508 把这项责任留给系统机制和操作系统:本地安全层必须限制谁能够取得并使用某个主体的凭据。RFC 1508 信息页 则保留了通用接口、语言绑定与后续版本之间的谱系。

使用凭据就是能够声明相应主体的身份。能否把这种能力交给另一个进程,不只是数据格式问题,而是本地授权问题。通用 API 可以统一函数边界,却不能替操作系统决定进程间的信任。

RFC 1511 从 Common Authentication Technology 工作组的历史目标解释这层分工:把安全实现细节与调用协议集成安全数据的工作分开。RFC 1511 信息页 将 RFC 1508、RFC 1509、Kerberos V5 和 DASS 放在同一组工作中。可移植性来自机制隐藏,不来自句柄全局化。

“没有凭据”反而会触发选择

GSS_C_NO_CREDENTIAL 的名字很容易诱导错误判断。应用没有传入显式凭据句柄,并不一定意味着匿名、空身份或没有权威。它是在请求本地实现选择默认凭据。

RFC 1509 把默认凭据如何建立、作用于哪些调用者留给实现。RFC 2078 在吸收实现经验后的 Version 2 中进一步说明默认主体解析及其授权检查;解析可能成功,也可能因为调用者无权取得凭据而失败。RFC 2078 信息页 保留这次版本替换关系。

所以零值或特殊常量不是“什么都没发生”。它启动了一个环境选择。审计必须记录调用者 PID、UID、服务账号或容器边界,本地实现与重启代次,默认规则,实际选中的机制、主体、用途和寿命。

默认值带来便利,也让权威更容易藏在调用代码之外。代码看似没有选择身份,本地代理却已经替它选择。

本地上下文句柄没有跨过网络

gss_ctx_id_t 同样可能相对于调用者解释。相同原子值在不同进程中可以代表不同安全上下文。上下文包含双方共同状态与密码学材料;RFC 1509 没有提供“再取一个现有上下文句柄”的普通操作。

跨越网络的是另一种对象:认证令牌。机制在一端生成受密码学保护、对调用应用不透明的位串,由应用自己的协议负责把它带给对端机制。令牌与本地句柄可能都“看不懂”,却有不同的移动规则和风险。

后续 C 绑定没有靠复制句柄解决跨进程迁移。RFC 2744 新增显式 export/import 路径:导出会让源进程中的上下文失活,生成供另一进程导入的进程间令牌,并要求同一上下文只能有一个活动实例。令牌可能包含密钥等敏感材料,必须受到保护,只交给可信进程。RFC 2744 信息页 记录它取代 RFC 1509。

这条演进本身就是证据。真正迁移上下文需要受控状态转换、敏感载荷与唯一活动实例约束;复制整数不具备这些语义。

可打印名字只是显示投影

RFC 1509 还把可打印名字和内部名字分开。字符串形式可能随配置或用户偏好变化;导入时要配合对象标识符说明命名空间。名字是否相等,应由 gss_compare_names 判断,而不是直接比较字符串。

RFC 2743 及其信息页 保留通用 Version 2 Update 1 的概念边界。RFC 2744 更明确地说,内部名字再次显示时,不必复现最初字符串,甚至不保证返回同一个命名空间标识符。

这使一条常见审计记录变得可疑:屏幕上的名字不是凭据本体,也不自动是应用账号。要把它用于归责,必须保存名字类型、机制、规范化或导入过程以及实际授权映射。

通道绑定只完成一个特定连接

RFC 1509 的 channel bindings 把发起方地址类型与地址、接受方地址类型与地址、应用数据拼接起来,由机制在建立上下文时签入。如果两端材料不一致,上下文可能无法建立。规范也提醒某些机制可能把绑定数据放进令牌,因此不应把机密内容直接用作绑定材料。

绑定成功只能证明这次机制交换接受了双方提交的那组材料。它不能证明地址的法律所有者、应用操作的授权、消息最终送达或业务效果。调用者仍须检查请求和返回的安全属性、主状态与机制专用状态,再由应用做独立授权。

RFC 2744 的安全考虑也没有把 GSS-API 描述成保证器:安全属性依赖底层机制和调用者是否正确检查返回值。RFC 1509 的 Datatracker 页面 只能证明文档状态,不能证明任何具名实现、部署、漏洞或当前产品行为。

薄接口为何反而要求厚回执

Heng Lu 的 Running-Code Primacy 强调,共同规范的价值必须在真实执行与结果中验证。Minimum Initial Specification, Localized Future Decision, and Voluntary Adoption 要求把共同层保持在必要范围,让未来选择留在本地。On Reality Layers, Symbolic Power, and Why Clarity Feels So Hostile 则要求不要让符号吞掉背后的对象、决定与效果。

RFC 1509 的 C 绑定正是一层薄接口。句柄保持不透明,使应用无需理解每个机制;本地系统继续控制凭据;对端令牌承担网络交换;应用保留业务授权。共同接口没有把这些角色合成一个“安全令牌”。

本组来源只证明规范文字与演进谱系。它们不证明某个实现采用哪种句柄范围,也不证明真实系统的默认身份、漏洞、互操作或部署结果。

历史教训因此非常具体:句柄可以是一次 API 调用的正确输入,却不能脱离调用者和解析状态,独自成为身份、权威或结果证据。

来源

  1. RFC 1509 信息页
  2. RFC 1509:GSS-API C 绑定
  3. RFC 1509 Datatracker 页面
  4. RFC 1508 信息页
  5. RFC 1508:Generic Security Service Application Program Interface
  6. RFC 1511 信息页
  7. RFC 1511:Common Authentication Technology Overview
  8. RFC 2078 信息页
  9. RFC 2078:GSS-API Version 2
  10. RFC 2743 信息页
  11. RFC 2743:GSS-API Version 2, Update 1
  12. RFC 2744 信息页
  13. RFC 2744:GSS-API Version 2 C 绑定
  14. Heng Lu:Running-Code Primacy
  15. Heng Lu:Minimum Initial Specification, Localized Future Decision, and Voluntary Adoption
  16. Heng Lu:On Reality Layers, Symbolic Power, and Why Clarity Feels So Hostile