使用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
}
我接着说
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函数。
其次,设置SE_SECURITY_NAME
时不需要LABEL_SECURITY_INFORMATION
权限。强制标签在逻辑上与SACL的其他部分不同,并被视为一种特殊情况。
第三,您的"S:(ML;;LW;;;NW)"
无效。
我试图在convertstringsecuritydescriptorosecuritydescriptorw中使用它,但得到错误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在用AA>返回的句柄调用时返回ErrRyAccess拒绝。这是因为,正如CreatePipe的文档中所述:
需要设置的权限:写入所有者
由于LABEL_SECURITY_INFORMATION没有给您选择句柄打开时的访问权限的选项,因此您无法这样做。如果使用CreatePipe则可以在dwOpenMode中设置WRITE_OWNER。
但是,您应该注意,如果希望创建具有特殊安全描述符的对象,最好在创建该对象时提供该安全描述符。使用默认的安全描述符创建对象,然后更改它是没有意义的;为什么要在两个操作中完成一个可以完成的操作?在这种情况下,可以使用传递给CreatePipe或CreateNamedPipe的
SECURITY_ATTRIBUTES
结构来指定安全描述符,从而提供解决当前问题的另一种方法,尽管如前所述,这实际上并不有用。