用于CNG密钥的DACL中的访问掩码的映射

时间:2016-12-05 23:03:41

标签: winapi permissions cng dacl

(注意:IMO问题主要是关于WinAPI和DACL而不是关于CNG,所以请继续阅读!)

我目前正在尝试修改Microsoft的加密提供程序开发工具包的示例CNG密钥存储提供程序,以便将密钥存储在单个文件中。但是,我遇到了可以分配给私钥的安全描述符。

在Windows Server Management Console的“证书管理单元”中,可以管理证书的私钥,即可以更改密钥的所有者,DACL和SACL,从而导致NCryptSetProperty调用带有安全描述符作为参数。对于DACL,管理单元仅允许允许/拒绝"完全控制"或"读取",这导致在ACE的访问掩码中设置GENERIC_ALL或GENERIC_READ位。

据我所知,这些通用位需要映射到特定于应用程序的权限 - 否则AccessCheck将无法正常工作。但我真的需要手工做这个吗???

CreatePrivateObjectSecurity + SetPrivateObjectSecurity并不总是有效,因为CreatePrivateObjectSecurity对输入安全描述符中的所有者和组非常挑剔。此外,当应用映射时,通用位在访问掩码中被清除,这导致管理单元显示错误的设置(正如我所说,管理单元在显示当前权限时仅考虑GA和GR位)。

似乎我在这里遗漏了一些东西......

1 个答案:

答案 0 :(得分:0)

在您PP_KEYSET_SEC_DESCR的{​​{1}}实施中,您获得了SECURITY_DESCRIPTOR的地址,您需要以某种方式将其应用于您的私钥存储。如果您的存储基于文件或注册表项(原则上是任何内核对象类型,但还可以在这里使用?),您需要使用文件或密钥HANDLE调用CPSetProvParam(必须具有WRITE_DAC访问权限(如果您说存储多个文件用于存储单个密钥,则可能是多次)。在内核GENERIC中,对象的访问权限将自动转换为对象特定的权限。

如果您的存储实现不是直接基于某个内核对象,而是自定义 - 此时您需要自己将GENERIC访问(0xF0000000掩码)转换为特定的访问权限(0x0000FFFF掩码)

__________________编辑____________________

经过更多检查后,我发现提供商不仅必须将通用转换为SetKernelObjectSecurity中的特定访问权限,还必须在CPSetProvParam中将特定转换为通用,尽管这不直接指向文档。

CPGetProvParam(在rsaenh.dll中实现)大致如此:

void CheckAndChangeAccessMask(PSECURITY_DESCRIPTOR SecurityDescriptor)
{
    BOOL bDaclPresent, bDaclDefaulted;
    PACL Dacl;
    ACL_SIZE_INFORMATION asi;

    if (
        GetSecurityDescriptorDacl(SecurityDescriptor, &bDaclPresent, &Dacl, &bDaclDefaulted) 
        &&
        bDaclPresent
        &&
        Dacl
        &&
        GetAclInformation(Dacl, &asi, sizeof(asi), AclSizeInformation)
        &&
        asi.AceCount
        )
    {

        union{
            PVOID pAce;
            PACE_HEADER pah;
            PACCESS_ALLOWED_ACE paa;
        };

        do 
        {
            if (GetAce(Dacl, --asi.AceCount, &pAce))
            {
                switch (pah->AceType)
                {
                case ACCESS_ALLOWED_ACE_TYPE:
                case ACCESS_DENIED_ACE_TYPE:
                    ACCESS_MASK Mask = paa->Mask, Gen_Mask = 0;

                    if (Mask & FILE_READ_DATA)
                    {
                        Gen_Mask |= GENERIC_READ;
                    }

                    if (Mask & FILE_WRITE_DATA)
                    {
                        Gen_Mask |= GENERIC_ALL;
                    }

                    paa->Mask = Gen_Mask;
                    break;
                }
            }
        } while (asi.AceCount);
    }
}

所以FILE_READ_DATA已转换为GENERIC_READFILE_WRITE_DATA转换为GENERIC_ALL(这正是算法) - 但您可以查看rsaenh.CheckAndChangeAccessMask的代码(来自pdb符号)

rsaenh首先通过MS_ENHANCED_PROVSE_FILE_OBJECT)从文件中获取SD,然后将其转换为特定于通用访问权限。 这里是调用图和修改后的DACL(在右上角,修改后的ACCESS_MASK为红色) GetNamedSecurityInfoW