SECURITY_ATTRIBUTES 结构和 CreateNamedPipe()
The SECURITY_ATTRIBUTES struct and CreateNamedPipe()
我的情况如下:使用 CreateNamedPipe()
创建命名管道对象的进程具有管理员权限,但使用 CreateFile()
的客户端进程 "connecting" 没有。将 NULL
作为最后一个参数传递给 CreateNamedPipe()
似乎默认为仅管理员访问权限。
作为 hack,我尝试在管道相关代码的持续时间内执行服务器端 ImpersonateLoggedOnUser()
/RevertToSelf()
方法,但它失败了。在我看来,这里最好的做法是为 CreateNamedPipe()
的最后一个参数实际设置一个正确的 SECURITY_ATTRIBUTES
结构,但我不知道如何做到这一点。
MSDN example 有一个关于注册表项操作的示例,但我缺乏专业知识来适应我的目的。
这是我试过的:
if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&pEveryoneSID))
{
_tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError());
ret_val = 0;
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow Everyone read access to the key.
ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
ea[0].grfAccessPermissions = STANDARD_RIGHTS_ALL;
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance = NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = (LPTSTR)pEveryoneSID;
// there's another ACE for administrators in between, but is of no relevance here
dwRes = SetEntriesInAcl(2, ea, NULL, &pACL);
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == pSD)
{
_tprintf(_T("LocalAlloc Error %u\n"), GetLastError());
ret_val = 0;
goto Cleanup;
}
if (!InitializeSecurityDescriptor(pSD,
SECURITY_DESCRIPTOR_REVISION))
{
_tprintf(_T("InitializeSecurityDescriptor Error %u\n"),
GetLastError());
ret_val = 0;
goto Cleanup;
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSD,
TRUE, // bDaclPresent flag
pACL,
FALSE)) // not a default DACL
{
_tprintf(_T("SetSecurityDescriptorDacl Error %u\n"),
GetLastError());
ret_val = 0;
goto Cleanup;
}
// Initialize a security attributes structure.
*sa = new SECURITY_ATTRIBUTES;
(*sa)->nLength = sizeof(SECURITY_ATTRIBUTES);
(*sa)->lpSecurityDescriptor = pSD;
(*sa)->bInheritHandle = FALSE;
结果是客户端在 CreateFile()
上收到错误 0x5
(访问被拒绝)。这里有什么问题?
您可以将描述符的 DACL 设置为 NULL,以允许任何人访问管道:
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (!pSD)
{
...
}
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
...
}
if (!SetSecurityDescriptorDacl(pSD, TRUE, NULL, FALSE))
{
...
}
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
... = CreateNamedPipe(..., &sa);
行 SetEntriesInAcl(2, ea, NULL, &pACL) 中出错;当需要 SetEntriesInAcl(1, ea, NULL, &pACL); - 你真的初始化并只使用 1 个条目。而不是检查 SetEntriesInAcl 返回的结果。
下一个代码将正确工作:
EXPLICIT_ACCESS ea = {
STANDARD_RIGHTS_ALL,SET_ACCESS, NO_INHERITANCE, { 0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, (LPTSTR)pEveryoneSID }
};
PACL pACL;
if (SetEntriesInAcl(1, &ea, NULL, &pACL) == ERROR_SUCCESS)
{
}
这里也可以被完整性级别拒绝访问。需要检查 os 版本,如果 vista+ 在安全描述符中设置 LowLabel。并且可以使用 0 DACL。 (默认情况下,如果未明确设置,系统会假定 MediumLabelSid,因为 LowIntegrity 客户端无法打开管道,但对于通常的非管理员客户端,@Remy Lebeau 的解决方案就足够了)
PSECURITY_DESCRIPTOR pSecurityDescriptor = (PSECURITY_DESCRIPTOR)alloca(SECURITY_DESCRIPTOR_MIN_LENGTH);
BOOL fOk = FALSE;
if (
InitializeSecurityDescriptor(pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)
&&
SetSecurityDescriptorDacl(pSecurityDescriptor, TRUE, 0, 0)
)
{
RTL_OSVERSIONINFOW rov = { sizeof (rov)};
if (0 <= RtlGetVersion(&rov))
{
if (rov.dwMajorVersion < 6)
{
fOk = TRUE;
}
else
{
PSID LowLabelSid = (PSID)alloca(64);
ULONG cbSid = 64;
if (CreateWellKnownSid(::WinLowLabelSid, 0, LowLabelSid, &cbSid))
{
::PACL LowLabelAcl = (::PACL)alloca(64+cbSid);
InitializeAcl(LowLabelAcl, 64+cbSid, ACL_REVISION);
if (AddMandatoryAce(LowLabelAcl, ACL_REVISION, 0, SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, LowLabelSid))
{
fOk = SetSecurityDescriptorSacl(pSecurityDescriptor, TRUE, LowLabelAcl, FALSE);
}
}
}
}
}
这是你的问题:
ea[0].grfAccessPermissions = STANDARD_RIGHTS_ALL;
STANDARD_RIGHTS_ALL
不是全部权限,只是全部standard rights,即删除,读控制,同步,写DAC,写所有者。特别是它不授予 FILE_READ_DATA
或 FILE_WRITE_DATA
,客户端需要这些才能读取 and/or 将数据写入管道。
我会推荐
ea[0].grfAccessPermissions = GENERIC_READ | FILE_WRITE_DATA;
并让客户端在打开管道时请求相同的访问权限。 (显然,如果这是出站管道,您可以省略 FILE_WRITE_DATA
权限,尽管在这种情况下默认权限应该没问题。)
我的情况如下:使用 CreateNamedPipe()
创建命名管道对象的进程具有管理员权限,但使用 CreateFile()
的客户端进程 "connecting" 没有。将 NULL
作为最后一个参数传递给 CreateNamedPipe()
似乎默认为仅管理员访问权限。
作为 hack,我尝试在管道相关代码的持续时间内执行服务器端 ImpersonateLoggedOnUser()
/RevertToSelf()
方法,但它失败了。在我看来,这里最好的做法是为 CreateNamedPipe()
的最后一个参数实际设置一个正确的 SECURITY_ATTRIBUTES
结构,但我不知道如何做到这一点。
MSDN example 有一个关于注册表项操作的示例,但我缺乏专业知识来适应我的目的。
这是我试过的:
if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&pEveryoneSID))
{
_tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError());
ret_val = 0;
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow Everyone read access to the key.
ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
ea[0].grfAccessPermissions = STANDARD_RIGHTS_ALL;
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance = NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = (LPTSTR)pEveryoneSID;
// there's another ACE for administrators in between, but is of no relevance here
dwRes = SetEntriesInAcl(2, ea, NULL, &pACL);
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == pSD)
{
_tprintf(_T("LocalAlloc Error %u\n"), GetLastError());
ret_val = 0;
goto Cleanup;
}
if (!InitializeSecurityDescriptor(pSD,
SECURITY_DESCRIPTOR_REVISION))
{
_tprintf(_T("InitializeSecurityDescriptor Error %u\n"),
GetLastError());
ret_val = 0;
goto Cleanup;
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSD,
TRUE, // bDaclPresent flag
pACL,
FALSE)) // not a default DACL
{
_tprintf(_T("SetSecurityDescriptorDacl Error %u\n"),
GetLastError());
ret_val = 0;
goto Cleanup;
}
// Initialize a security attributes structure.
*sa = new SECURITY_ATTRIBUTES;
(*sa)->nLength = sizeof(SECURITY_ATTRIBUTES);
(*sa)->lpSecurityDescriptor = pSD;
(*sa)->bInheritHandle = FALSE;
结果是客户端在 CreateFile()
上收到错误 0x5
(访问被拒绝)。这里有什么问题?
您可以将描述符的 DACL 设置为 NULL,以允许任何人访问管道:
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (!pSD)
{
...
}
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
...
}
if (!SetSecurityDescriptorDacl(pSD, TRUE, NULL, FALSE))
{
...
}
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
... = CreateNamedPipe(..., &sa);
行 SetEntriesInAcl(2, ea, NULL, &pACL) 中出错;当需要 SetEntriesInAcl(1, ea, NULL, &pACL); - 你真的初始化并只使用 1 个条目。而不是检查 SetEntriesInAcl 返回的结果。 下一个代码将正确工作:
EXPLICIT_ACCESS ea = {
STANDARD_RIGHTS_ALL,SET_ACCESS, NO_INHERITANCE, { 0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, (LPTSTR)pEveryoneSID }
};
PACL pACL;
if (SetEntriesInAcl(1, &ea, NULL, &pACL) == ERROR_SUCCESS)
{
}
这里也可以被完整性级别拒绝访问。需要检查 os 版本,如果 vista+ 在安全描述符中设置 LowLabel。并且可以使用 0 DACL。 (默认情况下,如果未明确设置,系统会假定 MediumLabelSid,因为 LowIntegrity 客户端无法打开管道,但对于通常的非管理员客户端,@Remy Lebeau 的解决方案就足够了)
PSECURITY_DESCRIPTOR pSecurityDescriptor = (PSECURITY_DESCRIPTOR)alloca(SECURITY_DESCRIPTOR_MIN_LENGTH);
BOOL fOk = FALSE;
if (
InitializeSecurityDescriptor(pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)
&&
SetSecurityDescriptorDacl(pSecurityDescriptor, TRUE, 0, 0)
)
{
RTL_OSVERSIONINFOW rov = { sizeof (rov)};
if (0 <= RtlGetVersion(&rov))
{
if (rov.dwMajorVersion < 6)
{
fOk = TRUE;
}
else
{
PSID LowLabelSid = (PSID)alloca(64);
ULONG cbSid = 64;
if (CreateWellKnownSid(::WinLowLabelSid, 0, LowLabelSid, &cbSid))
{
::PACL LowLabelAcl = (::PACL)alloca(64+cbSid);
InitializeAcl(LowLabelAcl, 64+cbSid, ACL_REVISION);
if (AddMandatoryAce(LowLabelAcl, ACL_REVISION, 0, SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, LowLabelSid))
{
fOk = SetSecurityDescriptorSacl(pSecurityDescriptor, TRUE, LowLabelAcl, FALSE);
}
}
}
}
}
这是你的问题:
ea[0].grfAccessPermissions = STANDARD_RIGHTS_ALL;
STANDARD_RIGHTS_ALL
不是全部权限,只是全部standard rights,即删除,读控制,同步,写DAC,写所有者。特别是它不授予 FILE_READ_DATA
或 FILE_WRITE_DATA
,客户端需要这些才能读取 and/or 将数据写入管道。
我会推荐
ea[0].grfAccessPermissions = GENERIC_READ | FILE_WRITE_DATA;
并让客户端在打开管道时请求相同的访问权限。 (显然,如果这是出站管道,您可以省略 FILE_WRITE_DATA
权限,尽管在这种情况下默认权限应该没问题。)