使用 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和评论说
要设置对象的 SACL,调用者必须启用 SE_SECURITY_NAME 权限。
:
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 是一个糟糕的选择。这个函数设计的不好,参数太少。没有创建双向管道或以异步模式打开管道的选项。我认为最好使用 CreateNamedPipeW 和 CreateFileW。
(或者,从 Windows 7 开始,您可以使用 ZwCreateNamedPipeFile 和 ZwOpenFile 创建和打开未命名的管道。)
发布的代码的最接近问题是 SetSecurityInfo和 SetKernelObjectSecurity使用 CreatePipe 返回的句柄调用时返回 ERROR_ACCESS_DENIED .这是因为,如 LABEL_SECURITY_INFORMATION 的文档中所述:
Right required to set: WRITE_OWNER
自 CreatePipe没有为您提供选择打开句柄的访问权限的选项,您无法执行此操作。如果您改为使用 CreateNamedPipe您可以在dwOpenMode 中设置WRITE_OWNER。
但是,您应该注意,如果您希望创建具有特殊安全描述符的对象,最好在创建对象时提供该安全描述符。使用默认安全描述符创建对象然后更改它是没有意义的;为什么要在两个操作中做一个可以做的事情?在这种情况下,您传递给 CreatePipe 或 CreateNamedPipe 的 SECURITY_ATTRIBUTES
结构可用于指定安全描述符,提供另一种方式来解决您的即时问题问题,尽管如前所述,这实际上没有用。
关于c - SetSecurityInfo 返回拒绝访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43876524/