How to Map Generic Rights to Standard and Specific Rights

There are two equivalent ways trustees can be assigned a specific set of permissions on Windows: via generic rights and via a combination of standard and specific rights.

Generic rights are identical for all object types. There are only four of them:

  • GENERIC_READ (0x80000000)
  • GENERIC_WRITE (0x40000000)
  • GENERIC_EXECUTE (0x20000000)
  • GENERIC_ALL (0x10000000)

As the name implies, generic rights are not object-specific and can thus easily be used by programmers to assign standard permission sets to any kind of object without having to worry about the peculiarities of files, registry keys, printers, and so on.

The exact meaning of each generic right depends on the object type, however. Each object type has a set of specific rights like FILE_LIST_DIRECTORY or PRINTER_ACCESS_USE. The exact names, their meaning and even the number of available specific rights depend on the capabilites of the object type. This is necessary – or what would you make of the right to add sub-objects to a printer? In other words, the generic rights provide a level of abstraction to the real capabilites of the differing object types.

Obviously there needs to be a mapping table somewhere that stores the association between each generic right and the set of standard plus specific rights it is equivalent to. Unfortunately that table is well hidden in the bowels of the operating system. Nicolas Sylvain was kind enough to publish an undocumented routine that programmatically determines the mappings. Interestingly some mappings change with the operating system version.

Without further ado, here is Nicolas’ code:

// Author: nsylvain@gmail.com (Nicolas Sylvain)
// Date: 1/12/2008
 
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif						
 
#include <windows.h>
#include <winternl.h>
#include <stdio.h>
 
//
// Declaration of undocumented structures.
//
 
#define STATUS_SUCCESS 0
 
typedef struct _OBJECT_TYPE_INFORMATION {
    UNICODE_STRING TypeName;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccessMask;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    ULONG PoolType;
    ULONG DefaultPagedPoolCharge;
    ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
 
typedef struct _OBJECT_ALL_TYPES_INFORMATION {
    ULONG NumberOfTypes;
    OBJECT_TYPE_INFORMATION TypeInformation[1];
} OBJECT_ALL_TYPES_INFORMATION, *POBJECT_ALL_TYPES_INFORMATION;
 
typedef enum _OBJECT_INFORMATION_CLASS {
    ObjectAllInformation = 3
} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
 
typedef NTSTATUS (WINAPI* NtQueryObjectFunction)(
    HANDLE Handle,
    OBJECT_INFORMATION_CLASS ObjectInformationClass,
    PVOID ObjectInformation,
    ULONG ObjectInformationLength,
    PULONG ReturnLength);
 
//
// End of undocumented structures.
// 
 
// Dumps the GENERIC_MAPPING structure for all object types on the system.
// If there is an error, the return value contains the win32 error code.
// Otherwise the function returns ERROR_SUCCESS.
DWORD DumpGenericMapping() {
  // Initialize the function pointer for NtQueryObject.
  HMODULE ntdll = ::LoadLibraryA("ntdll.dll");
  FARPROC ntqueryobject_ptr = ::GetProcAddress(ntdll, "NtQueryObject");
  NtQueryObjectFunction NtQueryObject =
      reinterpret_cast<NtQueryObjectFunction>(ntqueryobject_ptr);
 
  if (!NtQueryObject)
    return ERROR_UNIDENTIFIED_ERROR;
 
  // Query the length of the structure containing the information about the
  // types.
  // Starting on Windows Server 2003 the function fails if the input buffer is
  // NULL. It has to be at least 4 bytes big for the call to succeeded.
  // Any idea why?
  DWORD dummy = 0;
  DWORD size = sizeof(dummy);
  NTSTATUS status = NtQueryObject(NULL, ObjectAllInformation, &dummy, size, &size);
  if (0 == size)
    return ::GetLastError();
 
  BYTE* buffer = new BYTE[size];
 
  // Query the information about all the types.
  status = NtQueryObject(NULL, ObjectAllInformation, buffer, size, &size);
  if (status != STATUS_SUCCESS) {
    delete[] buffer;
    return ::GetLastError();
  }
 
  OBJECT_ALL_TYPES_INFORMATION* all_types =
      reinterpret_cast<OBJECT_ALL_TYPES_INFORMATION*>(buffer);
 
  // Print Header.
  wprintf(L"              Type Name All        Execute    Read       Write     \n");
  wprintf(L"-----------------------+----------+----------+----------+----------\n");
 
  OBJECT_TYPE_INFORMATION* current_type = all_types->TypeInformation;
  for (ULONG i = 0; i < all_types->NumberOfTypes; i++) {
    wprintf(L"%23.23wZ 0x%8.8X 0x%8.8X 0x%8.8X 0x%8.8X\n", 
            &current_type->TypeName,
            current_type->GenericMapping.GenericAll,
            current_type->GenericMapping.GenericExecute,
            current_type->GenericMapping.GenericRead,
            current_type->GenericMapping.GenericWrite);
 
 
    // Find the position of the next element in the structure.
    // The format of the structure is:
    //  -------------------
    // | Type1 Information | [sizeof(OBJECT_TYPE_INFORMATION)]
    // | Type1 Type Name   | [OBJECT_TYPE_INFORMATION.TypeName.MaximumLength]
    // | Alignement        | [0-3 Bytes]
    // | Type2 Information | 
    // //                  //
    // | TypeN Information |
    // | TypeN Type Name   |
    //  -------------------
    // The beginning of each type is aligned on 4 bytes boudary.
    //
    // Find the offset(aligned) to the next item;
    USHORT offset = (current_type->TypeName.MaximumLength+3) & ~3;
    // Find the end of the current type:
    BYTE* next_type = reinterpret_cast<BYTE*>(current_type->TypeName.Buffer) +
                      offset;
    current_type = reinterpret_cast<OBJECT_TYPE_INFORMATION*>(next_type);
  }
 
  delete[] buffer;
 
  return ERROR_SUCCESS;
}
 
//
// Sample Output
//
//
// Windows 2000 SP4
//              Type Name All        Execute    Read       Write
//-----------------------+----------+----------+----------+----------
//                   Type 0x000F0001 0x00020000 0x00020000 0x00020000
//              Directory 0x000F000F 0x00020003 0x00020003 0x0002000C
//           SymbolicLink 0x000F0001 0x00020001 0x00020001 0x00020000
//                  Token 0x000F01FF 0x00020000 0x00020008 0x000200E0
//                Process 0x001F0FFF 0x00120000 0x00020410 0x00020BEB
//                 Thread 0x001F03FF 0x00120000 0x00020048 0x00020037
//                    Job 0x001F03FF 0x00120000 0x00020004 0x0002000B
//                  Event 0x001F0003 0x00120000 0x00020001 0x00020002
//              EventPair 0x001F0000 0x00120000 0x00120000 0x00120000
//                 Mutant 0x001F0001 0x00120000 0x00020001 0x00020000
//               Callback 0x001F0001 0x00120000 0x00020000 0x00020001
//              Semaphore 0x001F0003 0x00120000 0x00020001 0x00020002
//                  Timer 0x001F0003 0x00120000 0x00020001 0x00020002
//                Profile 0x000F0001 0x00020001 0x00020001 0x00020001
//          WindowStation 0x000F037F 0x00020060 0x00020303 0x0002001C
//                Desktop 0x000F01FF 0x00020100 0x00020041 0x000200BE
//                Section 0x000F001F 0x00020008 0x00020005 0x00020002
//                    Key 0x000F003F 0x00020019 0x00020019 0x00020006
//                   Port 0x001F0001 0x00000000 0x00020001 0x00010001
//           WaitablePort 0x001F0001 0x00000000 0x00020001 0x00010001
//                Adapter 0x001F01FF 0x001200A0 0x00120089 0x00120116
//             Controller 0x001F01FF 0x001200A0 0x00120089 0x00120116
//                 Device 0x001F01FF 0x001200A0 0x00120089 0x00120116
//
// Windows XP SP2
//              Type Name All        Execute    Read       Write
//-----------------------+----------+----------+----------+----------
//                   Type 0x000F0001 0x00020000 0x00020000 0x00020000
//              Directory 0x000F000F 0x00020003 0x00020003 0x0002000C
//           SymbolicLink 0x000F0001 0x00020001 0x00020001 0x00020000
//                  Token 0x000F01FF 0x00020000 0x00020008 0x000200E0
//                Process 0x001F0FFF 0x00120000 0x00020410 0x00020BEB
//                 Thread 0x001F03FF 0x00120000 0x00020048 0x00020037
//                    Job 0x001F03FF 0x00120000 0x00020004 0x0002000B
//            DebugObject 0x001F000F 0x00120000 0x00020001 0x00020002
//                  Event 0x001F0003 0x00120000 0x00020001 0x00020002
//              EventPair 0x001F0000 0x00120000 0x00120000 0x00120000
//                 Mutant 0x001F0001 0x00120000 0x00020001 0x00020000
//               Callback 0x001F0001 0x00120000 0x00020000 0x00020001
//              Semaphore 0x001F0003 0x00120000 0x00020001 0x00020002
//                  Timer 0x001F0003 0x00120000 0x00020001 0x00020002
//                Profile 0x000F0001 0x00020001 0x00020001 0x00020001
//             KeyedEvent 0x000F0003 0x00020000 0x00020001 0x00020002
//          WindowStation 0x000F037F 0x00020060 0x00020303 0x0002001C
//                Desktop 0x000F01FF 0x00020100 0x00020041 0x000200BE
//                Section 0x000F001F 0x00020008 0x00020005 0x00020002
//                    Key 0x000F003F 0x00020019 0x00020019 0x00020006
//                   Port 0x001F0001 0x00000000 0x00020001 0x00010001
//           WaitablePort 0x001F0001 0x00000000 0x00020001 0x00010001
//                Adapter 0x001F01FF 0x001200A0 0x00120089 0x00120116
//             Controller 0x001F01FF 0x001200A0 0x00120089 0x00120116
//                 Device 0x001F01FF 0x001200A0 0x00120089 0x00120116
//                 Driver 0x001F01FF 0x001200A0 0x00120089 0x00120116
//           IoCompletion 0x001F0003 0x00120000 0x00020001 0x00020002
//                   File 0x001F01FF 0x001200A0 0x00120089 0x00120116
//                WmiGuid 0x00120FFF 0x00020010 0x00020001 0x00020002
//   FilterConnectionPort 0x001F0001 0x00000000 0x00020001 0x00010001
//FilterCommunicationPort 0x001F0001 0x00000000 0x00020001 0x00010001
//
// Windows Server 2003 SP2
//              Type Name All        Execute    Read       Write
//-----------------------+----------+----------+----------+----------
//                   Type 0x000F0001 0x00020000 0x00020000 0x00020000
//              Directory 0x000F000F 0x00020003 0x00020003 0x0002000C
//           SymbolicLink 0x000F0001 0x00020001 0x00020001 0x00020000
//                  Token 0x000F01FF 0x00020000 0x00020008 0x000200E0
//                Process 0x001F0FFF 0x00120000 0x00020410 0x00020BEB
//                 Thread 0x001F03FF 0x00120000 0x00020048 0x00020037
//                    Job 0x001F03FF 0x00120000 0x00020004 0x0002000B
//            DebugObject 0x001F000F 0x00120000 0x00020001 0x00020002
//                  Event 0x001F0003 0x00120000 0x00020001 0x00020002
//              EventPair 0x001F0000 0x00120000 0x00120000 0x00120000
//                 Mutant 0x001F0001 0x00120000 0x00020001 0x00020000
//               Callback 0x001F0001 0x00120000 0x00020000 0x00020001
//              Semaphore 0x001F0003 0x00120000 0x00020001 0x00020002
//                  Timer 0x001F0003 0x00120000 0x00020001 0x00020002
//                Profile 0x000F0001 0x00020001 0x00020001 0x00020001
//             KeyedEvent 0x000F0003 0x00020000 0x00020001 0x00020002
//          WindowStation 0x000F037F 0x00020060 0x00020303 0x0002001C
//                Desktop 0x000F01FF 0x00020100 0x00020041 0x000200BE
//                Section 0x000F001F 0x00020008 0x00020005 0x00020002
//                    Key 0x000F003F 0x00020019 0x00020019 0x00020006
//                   Port 0x001F0001 0x00000000 0x00020001 0x00010001
//           WaitablePort 0x001F0001 0x00000000 0x00020001 0x00010001
//                Adapter 0x001F01FF 0x001200A0 0x00120089 0x00120116
//             Controller 0x001F01FF 0x001200A0 0x00120089 0x00120116
//                 Device 0x001F01FF 0x001200A0 0x00120089 0x00120116
//                 Driver 0x001F01FF 0x001200A0 0x00120089 0x00120116
//           IoCompletion 0x001F0003 0x00120000 0x00020001 0x00020002
//                   File 0x001F01FF 0x001200A0 0x00120089 0x00120116
//                WmiGuid 0x00120FFF 0x00000010 0x00000001 0x00000002
//   FilterConnectionPort 0x001F0001 0x00000000 0x00020001 0x00010001
//FilterCommunicationPort 0x001F0001 0x00000000 0x00020001 0x00010001
//
// Windows Vista RTM and Windows Vista SP1
//              Type Name All        Execute    Read       Write
//-----------------------+----------+----------+----------+----------
//                   Type 0x000F0001 0x00020000 0x00020000 0x00020000
//              Directory 0x000F000F 0x00020003 0x00020003 0x0002000C
//           SymbolicLink 0x000F0001 0x00020001 0x00020001 0x00020000
//                  Token 0x000F01FF 0x00020005 0x0002001A 0x000201E0
//                    Job 0x001F001F 0x00120000 0x00020004 0x0002000B
//                Process 0x001FFFFF 0x00121001 0x00020410 0x00020BEA
//                 Thread 0x001FFFFF 0x00120800 0x00020048 0x00020437
//            DebugObject 0x001F000F 0x00120000 0x00020001 0x00020002
//                  Event 0x001F0003 0x00120000 0x00020001 0x00020002
//              EventPair 0x001F0000 0x00120000 0x00120000 0x00120000
//                 Mutant 0x001F0001 0x00120000 0x00020001 0x00020000
//               Callback 0x001F0001 0x00120000 0x00020000 0x00020001
//              Semaphore 0x001F0003 0x00120000 0x00020001 0x00020002
//                  Timer 0x001F0003 0x00120000 0x00020001 0x00020002
//                Profile 0x000F0001 0x00020001 0x00020001 0x00020001
//             KeyedEvent 0x000F0003 0x00020000 0x00020001 0x00020002
//          WindowStation 0x000F037F 0x00020060 0x00020303 0x0002001C
//                Desktop 0x000F01FF 0x00020100 0x00020041 0x000200BE
//        TpWorkerFactory 0x000F00FF 0x00020003 0x00020008 0x00020004
//                Adapter 0x001F01FF 0x001200A0 0x00120089 0x00120116
//             Controller 0x001F01FF 0x001200A0 0x00120089 0x00120116
//                 Device 0x001F01FF 0x001200A0 0x00120089 0x00120116
//                 Driver 0x001F01FF 0x001200A0 0x00120089 0x00120116
//           IoCompletion 0x001F0003 0x00120000 0x00020001 0x00020002
//                   File 0x001F01FF 0x001200A0 0x00120089 0x00120116
//                   TmTm 0x000F003F 0x00020000 0x00020001 0x0002001E
//                   TmTx 0x001F007F 0x00120018 0x00120001 0x0012007E
//                   TmRm 0x001F007F 0x0012005C 0x00120001 0x0012007E
//                   TmEn 0x000F001F 0x0002001C 0x00020001 0x0002001E
//                Section 0x000F001F 0x00020008 0x00020005 0x00020002
//                Session 0x000F0003 0x00120001 0x00020001 0x00020002
//                    Key 0x000F003F 0x00020039 0x00020019 0x00020006
//              ALPC Port 0x001F0001 0x00000000 0x00020001 0x00010001
//                WmiGuid 0x00120FFF 0x00000010 0x00000001 0x00000002
//        EtwRegistration 0x00020EFF 0x00020E90 0x0002000D 0x00020062
//   FilterConnectionPort 0x001F0001 0x00000000 0x00020001 0x00010001
//FilterCommunicationPort 0x001F0001 0x00000000 0x00020001 0x00010001

, , ,

Trackbacks/Pingbacks

  1. The Making of HelgeKlein.com, SetACL’s New Home | Helge Klein - October 31, 2010

    […] How to Map Generic Rights to Standard and Specific Rights […]

Leave a Reply