安全标识符

Windows 使用安全标识符 (SID) 作为最终值来区分安全实体。 例如,为系统上的单个用户创建的每个新帐户分配唯一的安全标识符。 对于文件系统,仅使用此 SID。

下图说明了安全标识符结构。

说明安全标识符结构的关系图。

除了唯一的 SID,Windows 系统还定义了一组已知的标识符。 例如,本地管理员是一个已知的 SID。

Windows 提供了一种内核内机制,用于在内核环境中在 SID 和用户名之间进行转换。 这些函数调用可从 ksecdd 驱动程序获取,该驱动程序使用用户模式帮助程序服务实现这些函数。 因此,在文件系统中使用它们必须遵守与用户模式服务通信的通常规则。 这些调用不能在分页文件 I/O 期间使用。

其中一些函数包括:

  • SecMakeSPN 创建一个服务提供商名称字符串,可在与特定安全服务提供商通信时使用。

  • SecMakeSPNEx 是 Windows XP 中引入的 SecMakeSPN 的增强版本。

  • SecMakeSPNEx2 是从 Windows Vista 和 Windows Server 2008 开始提供的 SecMakeSPNEx 的增强版本。

  • SecLookupAccountSid 返回指定 SID 的帐户名称。

  • SecLookupAccountName 检索指定帐户名称的 SID。

  • SecLookupWellKnownSid 返回指定的已知 SID 类型的正确 SID。 此功能在 Windows Server 2003 及更高版本上可用。

此外,任何内核驱动程序都可以使用以下标准运行时库例程创建 SID:

  • RtlInitializeSid 为新 SID 初始化缓冲区。

  • RtlLengthSid 确定存储在给定缓冲区中的 SID 的大小。

  • RtlValidSid 确定给定的 SID 缓冲区是否为有效的格式化缓冲区。

RtlLengthSidRtlValidSid 假定存在 SID 的 8 字节固定标头。 因此,在调用这些函数之前,驱动程序应检查 SID 标头的最小长度。

虽然还有其他几个 RTL 函数,但此列表提供了构造 SID 时所需的主要函数。

下面的代码示例演示如何为“本地系统”实体创建 SID。 还可以使用 Windows Server 2003 中引入的更简单的 SecLookupWellKnownSid 函数。

{
    //
    // temporary stack-based storage for an SID
    //
    UCHAR sidBuffer[128];
    PISID localSid = (PISID) sidBuffer;
    SID_IDENTIFIER_AUTHORITY localSidAuthority = 
        SECURITY_NT_AUTHORITY;

    //
    // build the local system SID
    //
    RtlZeroMemory(sidBuffer, sizeof(sidBuffer));
 
    localSid->Revision = SID_REVISION;
    localSid->SubAuthorityCount = 1;
    localSid->IdentifierAuthority = localSidAuthority;
    localSid->SubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID;
 
    //
    // make sure it is valid
    //
    if (!RtlValidSid(localSid)) {
        DbgPrint("no dice - SID is invalid\n");
        return(1);
    }
}

下面的代码示例演示如何使用“本地系统”实体的 SecLookupWellKnownSid 函数创建 SID:

{
    UCHAR sidBuffer[128];
    PISID localSid = (PISID) sidBuffer;
    SIZE_T sidSize;
    status = SecLookupWellKnownSid(WinLocalSid,
                                   &localSid,
                                   sizeof(sidBuffer),
                                   &sidSize);

    if (!NT_SUCCESS(status)) {
      //
      // error handling
      //
    }
  }

这两种方法都是有效的,但后一种代码是首选。 这些代码示例使用本地缓冲区来存储 SID。 这些缓冲区不能在当前调用上下文之外使用。 如果 SID 缓冲区需要持久化,则应从池内存中分配缓冲区。