SetSecurityInfo returns 访问被拒绝

SetSecurityInfo returns access denied

使用 C,我试图在进程与其子进程之间建立管道连接,而子进程具有较低的强制(完整性)级别(低,而父进程为高)。

我写了下面的程序(如果它是一个简化版本),但它失败了:ERROR_ACCESS_DENIED (0x5)

INT wmain(IN SIZE_T nArgc, IN PWSTR *pArgv)
{
    SECURITY_ATTRIBUTES securityArrtibutes = { 0 };
    HANDLE hPipeRead = NULL;
    HANDLE hPipeWrite = NULL;

    tSecurityArrtibutes.nLength = sizeof(tSecurityArrtibutes);
    tSecurityArrtibutes.bInheritHandle = TRUE;

    SetSeSecurityNamePrivilege();
    CreatePipe(&hPipeRead, &hPipeWrite, &securityArrtibutes, 0);
    ChangeMandatoryLabelHandle(hPipeRead);
}

VOID ChangeMandatoryLabelHandle(HANDLE hObject)
{
    BOOL bRetval = FALSE;
    DWORD dwError = 0;
    PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
    PACL ptSacl = NULL;
    BOOL bSaclPresent = FALSE;
    BOOL bSaclDefaulted = FALSE;
    PWSTR pSDDL = NULL;

    SDDL = L"S:(ML;;LW;;;NW)";

    bRetval = ConvertStringSecurityDescriptorToSecurityDescriptorW(pSDDL, SDDL_REVISION_1, &pSecurityDescriptor, NULL);
    if (FALSE == bRetval)
        ... // Handle failure

    bRetval = GetSecurityDescriptorSacl(pSecurityDescriptor, &bSaclPresent, &ptSacl, &bSaclDefaulted);
    if (FALSE == bRetval)
        ... // Handle failure

    // getting ERROR_ACCESS_DENIED (0x5)
    dwErr = SetSecurityInfo(hObject, SE_KERNEL_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, ptSacl);
    if (ERROR_SUCCESS != dwErr)
        ... // Handle failure

    ... // Cleanup
}

我遵循了 https://msdn.microsoft.com/en-us/library/windows/desktop/aa379588(v=vs.85).aspx 和评论 To set the SACL of an object, the caller must have the SE_SECURITY_NAME privilege enabled. :

BOOL SetSeSecurityNamePrivilege()
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    LUID luid;

    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_IMPERSONATE, &hToken)
        return FALSE

    if (!LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &luid))
        return FALSE;

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if (bEnablePrivilege)
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
        return FALSE;

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
        return FALSE;

    return TRUE;
}

注意:当我尝试使用文件执行它时,我得到了相同的结果,使用 CreateFile 而不是 CreatePipe。 此外,如果我尝试对文件执行此操作,并将 SetSecurityInfo 替换为 SetNamedSecurityInfoW,并为其提供文件的完整路径,效果很好。

有没有人知道如何让它发挥作用?谢谢!

解决您当前问题的原因之前的几点注意事项。

首先,您根本不需要更改安全描述符,这样做不太可能帮助您实现最终目标。仅当您尝试打开对象的句柄时才检查安全描述符;如果您已经有句柄,则安全描述符无效。由于您正在创建一个未命名的管道,因此您必须将句柄而不是管道名称传递给子管道,因此您根本不需要 ChangeMandatoryLabelHandle 函数。

其次,设置LABEL_SECURITY_INFORMATION时不需要SE_SECURITY_NAME权限。强制性标签在逻辑上与 SACL 的其余部分不同,并被视为特殊情况。

第三,您的"S:(ML;;LW;;;NW)"无效。

我试图在 ConvertStringSecurityDescriptorToSecurityDescriptorW 中使用它并得到错误 1336,访问控制列表 (ACL) 结构无效。 相反,使用"D:NO_ACCESS_CONTROLS:(ML;;;;;LW)" 或更好地仍然使用以下代码来创建具有低标签且没有 DACL 的安全描述符:

ULONG cb = MAX_SID_SIZE;
PSID LowLabelSid = (PSID)alloca(MAX_SID_SIZE);

ULONG dwError = NOERROR;

if (CreateWellKnownSid(WinLowLabelSid, 0, LowLabelSid, &cb))
{
    PACL Sacl = (PACL)alloca(cb += sizeof(ACL) + sizeof(ACE_HEADER) + sizeof(ACCESS_MASK));

    if (InitializeAcl(Sacl, cb, ACL_REVISION) && 
        AddMandatoryAce(Sacl, ACL_REVISION, 0, 0, LowLabelSid))
    {
        SECURITY_DESCRIPTOR sd;
        InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
        SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
        SetSecurityDescriptorSacl(&sd, TRUE, Sacl, FALSE);

        SECURITY_ATTRIBUTES sa = { sizeof(sa), &sd, TRUE };

        // todo something here
    }
    else
    {
        dwError = GetLastError();
    }
}
else
{
    dwError = GetLastError();
}

但是,您需要再次了解,为未命名对象创建安全描述符(几乎)从来没有任何意义。仅在打开对象时检查安全描述符,并且(在用户模式下)您无法打开没有名称的对象。

(从内核模式我们可以使用ObOpenObjectByPointer通过指针打开一个对象。)

(在旧版本的Windows中,CreatePipe实际上创建了一个随机名称的管道,但是从Windows 7开始管道确实是未命名的, 所以它不能用 CreateFile 或任何类似的方法打开。)

无论如何,我认为在这种情况下使用 CreatePipe 是一个糟糕的选择。这个函数设计的不好,参数太少。没有创建双向管道或以异步模式打开管道的选项。我认为使用 CreateNamedPipeWCreateFileW 更好。

(或者,从 Windows 7 开始,您可以使用 ZwCreateNamedPipeFileZwOpenFile 创建并打开一个未命名的管道.)


发布的代码的最接近问题是 SetSecurityInfo and SetKernelObjectSecurity return ERROR_ACCESS_DENIED when called with the handle returned by CreatePipe. This is because, as described in the documentation for LABEL_SECURITY_INFORMATION:

Right required to set: WRITE_OWNER

因为CreatePipe does not give you the option to select the access rights that the handles are opened with, you have no way of doing this. If you instead use CreateNamedPipe你可以在dwOpenMode.

中设置WRITE_OWNER

但是,您应该注意,如果您希望创建具有特殊安全描述符的对象,最好在创建对象时提供该安全描述符。使用默认安全描述符创建对象然后更改它是没有意义的;为什么要在两个操作中做一个可以做的事情?在这种情况下,您传递给 CreatePipeCreateNamedPipeSECURITY_ATTRIBUTES 结构可用于指定安全描述符,提供另一种方式解决您眼前的问题,尽管如前所述,这实际上没有用。