Reputation: 12486
Windows 7
I am reading the Windows via C\C++ book. I try to understand how to work the security settings of the boundary descriptor, private namespace, and core object.
The boundary descriptor gets a name, but more importantly, it gets a SID of a privileged user group that is associated with it. That way, Windows ensures that only the applications running under the context of a user that is a part of this privileged group is able to create the same namespace in the same boundary and thereby access the kernel objects created within this boundary that are prefixed with the private namespace name.
This is the simple code sample of creating a core object in the private namespace (from the book):
void CheckInstances() {
// Create the boundary descriptor
g_hBoundary = CreateBoundaryDescriptor(g_szBoundary, 0);
// Create a SID corresponding to the Local Administrator group
BYTE localAdminSID[SECURITY_MAX_SID_SIZE];
PSID pLocalAdminSID = &localAdminSID;
DWORD cbSID = sizeof(localAdminSID);
if (!CreateWellKnownSid(
WinBuiltinAdministratorsSid, NULL, pLocalAdminSID, &cbSID)) {
AddText(TEXT("AddSIDToBoundaryDescriptor failed: %u\r\n"),
GetLastError());
return;
}
// Associate the Local Admin SID to the boundary descriptor
// --> only applications running under an administrator user
// will be able to access the kernel objects in the same namespace
if (!AddSIDToBoundaryDescriptor(&g_hBoundary, pLocalAdminSID)) {
AddText(TEXT("AddSIDToBoundaryDescriptor failed: %u\r\n"),
GetLastError());
return;
}
// Create the namespace for Local Administrators only
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
TEXT("D:(A;;GA;;;BA)"),
SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL)) {
AddText(TEXT("Security Descriptor creation failed: %u\r\n"), GetLastError());
return;
}
g_hNamespace =
CreatePrivateNamespace(&sa, g_hBoundary, g_szNamespace);
// Don't forget to release memory for the security descriptor
LocalFree(sa.lpSecurityDescriptor);
// Check the private namespace creation result
DWORD dwLastError = GetLastError();
if (g_hNamespace == NULL) {
// Nothing to do if access is denied
// --> this code must run under a Local Administrator account
if (dwLastError == ERROR_ACCESS_DENIED) {
AddText(TEXT("Access denied when creating the namespace.\r\n"));
AddText(TEXT(" You must be running as Administrator.\r\n\r\n"));
return;
} else {
if (dwLastError == ERROR_ALREADY_EXISTS) {
// If another instance has already created the namespace,
// we need to open it instead.
AddText(TEXT("CreatePrivateNamespace failed: %u\r\n"), dwLastError);
g_hNamespace = OpenPrivateNamespace(g_hBoundary, g_szNamespace);
if (g_hNamespace == NULL) {
AddText(TEXT(" and OpenPrivateNamespace failed: %u\r\n"),
dwLastError);
return;
} else {
g_bNamespaceOpened = TRUE;
AddText(TEXT(" but OpenPrivateNamespace succeeded\r\n\r\n"));
}
} else {
AddText(TEXT("Unexpected error occurred: %u\r\n\r\n"),
dwLastError);
return;
}
}
}
// Try to create the mutex object with a name
// based on the private namespace
TCHAR szMutexName[64];
StringCchPrintf(szMutexName, _countof(szMutexName), TEXT("%s\\%s"),
g_szNamespace, TEXT("Singleton"));
g_hSingleton = CreateMutex(NULL, FALSE, szMutexName);
if (GetLastError() == ERROR_ALREADY_EXISTS) {
// There is already an instance of this Singleton object
AddText(TEXT("Another instance of Singleton is running:\r\n"));
AddText(TEXT("--> Impossible to access application features.\r\n"));
} else {
// First time the Singleton object is created
AddText(TEXT("First instance of Singleton:\r\n"));
AddText(TEXT("--> Access application features now.\r\n"));
}
}
The boundary descriptor has a SID. User can't cross this boundary if he is not a member of descriptor's SID. But the private namespace also has own security settings (through the SECURITY_ATTRIBUTES)... And the core object (the mutex in the code sample) has the same...
Why only the security settings of the boundary descriptor is not enough?
Upvotes: 1
Views: 926
Reputation: 36318
There are two interpretations of your question, and I'm going to answer both because I think they're both important.
For one, because everything in Windows has a security descriptor. Well, not everything, but all kernel objects. Making an exception for private namespaces would likely be a great deal of trouble and would serve no useful purpose.
There may also be edge cases where the security descriptors are in fact useful. A boundary descriptor doesn't allow for deny entries, for instance, and there may be scenarios involving impersonation where it is useful to have a security descriptor distinct from the boundary descriptor.
We can also speculate that Microsoft may have been thinking about forwards compatibility (the security descriptor might be necessary for a feature that they may wish to add in the future) and defense-in-depth (the security descriptor might one day prevent or limit exploitation if someone discovers a bug in the code that implements the boundary descriptors).
Defense-in-depth. We don't actually know that there is no way to exploit a private namespace with inappropriate permissions, after all; all we know is that we haven't found any documented way to do so. For example, there might be undocumented functions (or security flaws) that allow you to fake your way past the boundary descriptor, or to change it.
I would strongly recommend that you always ensure your private namespace objects are given ACLs appropriate to their purpose.
Upvotes: 1