Andrey Bushman
Andrey Bushman

Reputation: 12486

Why only the security settings of the boundary descriptor is not enough?

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

Answers (1)

Harry Johnston
Harry Johnston

Reputation: 36318

There are two interpretations of your question, and I'm going to answer both because I think they're both important.

  • Why do private namespaces have security descriptors?

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).

  • Why does the sample code set a specific ACL rather than just leaving it empty?

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

Related Questions