C-C++ Code Example: Retrieving the Access Rights of a Queue

 

Applies To: Windows 10, Windows 7, Windows 8, Windows 8.1, Windows Server 2008, Windows Server 2008 R2, Windows Server 2012, Windows Server 2012 R2, Windows Server Technical Preview, Windows Vista

The following example provides three application-defined functions that can be used for retrieving and displaying lists of the permissions granted to each user account mentioned in the security descriptor of a queue.

The first function, GetQSecurity, receives the computer name and queue name specified by the user as input parameters and calls MQGetQueueSecurity with the SecurityInformation parameter set to DACL_SECURITY_INFORMATION to obtain the discretionary access control list (DACL) from the queue's security descriptor and store it in a SECURITY_DESCRIPTOR structure. MQGetQueueSecurity can be used to obtain security information for a public queue on the local or a remote computer, or for a private queue on the local computer.

The second function, DisplayDaclInfo, retrieves the DACL from the security descriptor buffer and stores it in an ACL structure and then retrieves the individual access control entries (ACEs) from the ACL structure. This function can retrieve data only from simple access-allowed and access-denied ACEs, and not from callback and object-specific ACEs.

The third function, DisplayPermissions, displays the permissions granted or denied to the applicable trustee based on the access rights contained in the access mask that is passed as an input parameter.

For information about queue security, see Access Control.

An application using these functions must include the Windows.h, Stdio.h, and Mq.h header files.

To retrieve the queue's DACL and store it in a security descriptor buffer

  1. Validate the input strings.

Note

It is the responsibility of the caller to ensure that these strings contain only valid characters and are null-terminated.

  1. Define the variables needed to obtain the format name.

  2. Define the variables needed to retrieve information from the queue's security descriptor.

  3. Generate the complete path name of the queue from the computer name and queue name passed to the function.

  4. Call MQPathNameToFormatName to obtain the public or private format name of the queue from its path name. This format name is used to obtain the information from the queue's security descriptor.

Note

A direct format name can be used to obtain information from the security descriptor of a local private queue only.

  1. In a loop, call MQGetQueueSecurity twice with the SecurityInformation parameter set to DACL_SECURITY_INFORMATION to obtain the discretionary access control list (DACL) from the queue's security descriptor. The first call is made with a one-byte buffer to find out the size of the buffer needed. The second call, which is made after memory is allocated for the self-relative SECURITY_DESCRIPTOR structure needed to store the information, retrieves the component of security information requested (the DACL) and stores it in the security descriptor buffer.

  2. Call DisplayDaclInfo, the second function described in this example, to retrieve the DACL from the security descriptor buffer.

  3. Free the memory allocated for the security descriptor buffer.

Code Example

The following code example can be run on computers with Windows NT® 4.0, Windows® 2000, and newer operating systems that have Message Queuing installed.

HRESULT GetQSecurity(  
                     LPCWSTR wszComputerName,  
                     LPCWSTR wszQueueName  
                     )  
{  
  
  // Validate the input strings.  
  if (wszComputerName == NULL || wszQueueName == NULL)  
  {  
    return MQ_ERROR_INVALID_PARAMETER;  
  }  
  
  // Define the variables needed to obtain the format name.  
  DWORD dwPathNameLength = 0;  
  WCHAR * wszPathName = NULL;  
  DWORD dwFormatNameBufferLength = 256;             // Format name buffer length  
  WCHAR wszFormatNameBuffer[256];                   // Format name buffer  
  
  // Define the variables needed to retrieve the security information.  
  PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;  // Pointer to the security descriptor buffer  
  DWORD dwBufferLength = 1;  
  DWORD dwBufferLengthNeeded = 1;  
  HRESULT hr = MQ_OK;                               // Define results  
  
  // Generate the complete path name of the queue.  
  dwPathNameLength = wcslen(wszComputerName) + wcslen(wszQueueName) + 2;  
  wszPathName = new WCHAR[dwPathNameLength];  
  if (wszPathName == NULL)  
  {  
    return MQ_ERROR_INSUFFICIENT_RESOURCES;  
  }  
  memset(wszPathName, 0, dwPathNameLength*sizeof(WCHAR));  
  // ************************************  
  // You must concatenate wszComputerName, "\", and wszQueueName into   
  // the wszPathName buffer.  
  // wszPathName = wszComputerName + "\" + wszQueueName  
  // If the computer name is to long for the bugger, return MQ_ERROR.  
  // ************************************  
  
  wszPathName[dwPathNameLength - 1] = L'\0';  
  
  // Obtain the queue's format name from its path name.  
  hr = MQPathNameToFormatName(  
                              wszPathName,  
                              wszFormatNameBuffer,  
                              &dwFormatNameBufferLength  
                              );  
  if (FAILED(hr))  
  {  
    wprintf(L"The call to MQPathNameToFormatName failed. Error code: 0x%X\n", hr);  
    delete [] wszPathName;  
    return hr;  
  }  
  
  // Display some header information and free the memory allocated  
  // for the path name buffer.  
  wprintf(L"Retrieving queue security information for %s...\n\n", wszPathName );  
  delete [] wszPathName;  
  
  // Retrieve the DACL from the queue's security descriptor.  
  for ( ; ; )  
  {  
    pSecurityDescriptor = (PSECURITY_DESCRIPTOR) new byte[dwBufferLength];  
    hr = MQGetQueueSecurity(  
                            wszFormatNameBuffer,   
                            DACL_SECURITY_INFORMATION,  // Retrieving only the DACL  
                            pSecurityDescriptor,  
                            dwBufferLength,  
                            &dwBufferLengthNeeded  
                            );  
    if (SUCCEEDED(hr))  
    {  
      break;  
    }  
    if (hr == MQ_ERROR_SECURITY_DESCRIPTOR_TOO_SMALL)  
    {  
  
      // Allocate the memory needed for the security descriptor buffer.  
      delete [] pSecurityDescriptor;  
      dwBufferLength = dwBufferLengthNeeded;  
      pSecurityDescriptor = (PSECURITY_DESCRIPTOR) new byte[dwBufferLength];  
      if(pSecurityDescriptor == NULL)  
      {  
        wprintf(L"Memory could not be allocated for the security descriptor buffer.\n" );  
        return MQ_ERROR_INSUFFICIENT_RESOURCES;  
      }  
      memset(pSecurityDescriptor, 0, dwBufferLength);  
      continue;  
    }  
    wprintf(L"The call to MQGetQueueSecurity failed. Error code: 0x%X\n", hr);  
    delete [] pSecurityDescriptor;  
    return hr;  
  }  
  
  hr = DisplayDaclInfo(pSecurityDescriptor, wszComputerName);  
  
  if (FAILED(hr))  
  {  
    wprintf(L"DisplayDaclInfo failed. Error code: 0x%X\n", hr);  
  }  
  else hr = MQ_OK;  
  
  //Free the memory allocated for the security descriptor buffer.  
  delete [] pSecurityDescriptor;  
  return hr;  
}  

To retrieve the DACL from the security descriptor buffer and store it in an ACL structure

  1. Validate the input parameters.

  2. Create buffers that may be large enough for retrieving the domain name and account name of a trustee.

  3. Call GetSecurityDescriptorDacl to obtain a pointer to the DACL in the security descriptor buffer. If the security descriptor contains a DACL, the function sets pDacl to the address of the DACL in the security descriptor buffer. If the security descriptor does not contain a DACL, the function sets pDacl to NULL.

  4. Call GetAclInformation to retrieve information about the DACL in an ACL_SIZE_INFORMATION structure. This information includes the number of ACEs in the DACL.

  5. Create buffers for the domain name and the account name.

  6. In a loop, call GetAce to retrieve a pointer to each successive ACE in the DACL, call LookupAccountSid to obtain the domain name and account name that correspond to the SID in the ACE, and call DisplayPermissions (the third function described in this example) to display the permissions that are contained in the access mask found in the ACE.

  7. Free the memory allocated for the domain name and account name buffers.

HRESULT DisplayDaclInfo(  
                        PSECURITY_DESCRIPTOR pSecurityDescriptor,  
                        LPCWSTR wszComputerName  
                        )  
{  
  
  // Validate the input parameters.  
  if (pSecurityDescriptor == NULL || wszComputerName == NULL)  
  {  
    return MQ_ERROR_INVALID_PARAMETER;  
  }  
  
  PACL pDacl = NULL;  
  ACL_SIZE_INFORMATION aclsizeinfo;  
  ACCESS_ALLOWED_ACE * pAce = NULL;  
  SID_NAME_USE eSidType;  
  DWORD dwErrorCode = 0;  
  HRESULT hr = MQ_OK;  
  
  // Create buffers that may be large enough.  
  const DWORD INITIAL_SIZE = 256;  
  DWORD cchAccName = 0;  
  DWORD cchDomainName = 0;  
  DWORD dwAccBufferSize = INITIAL_SIZE;  
  DWORD dwDomainBufferSize = INITIAL_SIZE;  
  DWORD cAce;  
  WCHAR * wszAccName = NULL;  
  WCHAR * wszDomainName = NULL;  
  
  // Retrieve a pointer to the DACL in the security descriptor.  
  BOOL fDaclPresent = FALSE;  
  BOOL fDaclDefaulted = TRUE;  
  if (GetSecurityDescriptorDacl(  
                                pSecurityDescriptor,  
                                &fDaclPresent,  
                                &pDacl,  
                                &fDaclDefaulted  
                                ) == FALSE)  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"GetSecurityDescriptorDacl failed. GetLastError returned: %d\n", dwErrorCode);  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
  // Check whether no DACL or a NULL DACL was retrieved from the security descriptor buffer.  
  if (fDaclPresent == FALSE || pDacl == NULL)  
  {  
    wprintf(L"No DACL was found (all access is denied), or a NULL DACL (unrestricted access) was found.\n");  
    return MQ_OK;  
  }  
  
  // Retrieve the ACL_SIZE_INFORMATION structure to find the number of ACEs in the DACL.  
  if (GetAclInformation(  
                        pDacl,  
                        &aclsizeinfo,  
                        sizeof(aclsizeinfo),  
                        AclSizeInformation  
                        ) == FALSE)  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"GetAclInformation failed. GetLastError returned: %d\n", dwErrorCode);  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
  // Create buffers for the account name and the domain name.  
  wszAccName = new WCHAR[dwAccBufferSize];  
  if (wszAccName == NULL)  
  {  
    return MQ_ERROR_INSUFFICIENT_RESOURCES;  
  }  
  wszDomainName = new WCHAR[dwDomainBufferSize];  
  if (wszDomainName == NULL)  
  {  
    return MQ_ERROR_INSUFFICIENT_RESOURCES;  
  }  
  memset(wszAccName, 0, dwAccBufferSize*sizeof(WCHAR));  
  memset(wszDomainName, 0, dwDomainBufferSize*sizeof(WCHAR));  
  
  // Set the computer name string to NULL for the local computer.  
  if (wcscmp(wszComputerName, L".") == 0)  
  {  
    wszComputerName = L"\0";  
  }  
  
  // Loop through the ACEs and display the information.  
  for (cAce = 0; cAce < aclsizeinfo.AceCount && hr == MQ_OK; cAce++)  
  {  
  
    // Get ACE info  
    if (GetAce(  
               pDacl,  
               cAce,  
               (LPVOID*)&pAce  
               ) == FALSE)  
    {  
      wprintf(L"GetAce failed. GetLastError returned: %d\n", GetLastError());  
      continue;  
    }  
  
    // Obtain the account name and domain name for the SID in the ACE.  
    for ( ; ; )  
    {  
  
      // Set the character-count variables to the buffer sizes.  
      cchAccName = dwAccBufferSize;  
      cchDomainName = dwDomainBufferSize;  
      if (LookupAccountSidW(  
                            wszComputerName, // NULL for the local computer  
                            &pAce->SidStart,  
                            wszAccName,  
                            &cchAccName,  
                            wszDomainName,  
                            &cchDomainName,  
                            &eSidType  
                            ) == TRUE)  
      {  
        break;  
      }  
  
      // Check if one of the buffers was too small.  
      if ((cchAccName > dwAccBufferSize) || (cchDomainName > dwDomainBufferSize))  
      {  
  
        // Reallocate memory for the buffers and try again.  
        wprintf(L"The name buffers were too small. They will be reallocated.\n");  
        delete [] wszAccName;  
        delete [] wszDomainName;  
        wszAccName = new WCHAR[cchAccName];  
        if (wszAccName == NULL)  
        {  
          return MQ_ERROR_INSUFFICIENT_RESOURCES;  
        }  
        wszDomainName = new WCHAR[cchDomainName];  
        if (wszDomainName == NULL)  
        {  
          return MQ_ERROR_INSUFFICIENT_RESOURCES;  
        }  
        memset(wszAccName, 0, cchAccName*sizeof(WCHAR));  
        memset(wszDomainName, 0, cchDomainName*sizeof(WCHAR));  
        dwAccBufferSize = cchAccName;  
        dwDomainBufferSize = cchDomainName;  
        continue;  
      }  
  
      // Something went wrong in the call to LookupAccountSid.  
      // Check if an unexpected error occurred.  
      if (GetLastError() == ERROR_NONE_MAPPED)  
      {  
        wprintf(L"An unexpected error occurred during the call to LookupAccountSid. A name could not be found for the SID.\n" );  
        wszDomainName[0] = L'\0';  
        if (dwAccBufferSize > wcslen(L"!Unknown!"))  
        {  
             // ************************************  
             // You must copy the string "!Unknown!" into the   
             // wszAccName buffer.  
             // ************************************  
  
             wszAccName[dwAccBufferSize - 1] = L'\0';  
        }  
        break;  
      }  
      else  
      {  
        dwErrorCode = GetLastError();  
        wprintf(L"LookupAccountSid failed. GetLastError returned: %d\n", dwErrorCode);  
        delete [] wszAccName;  
        delete [] wszDomainName;  
        return HRESULT_FROM_WIN32(dwErrorCode);  
      }  
    }  
  
    switch(pAce->Header.AceType)  
    {  
      case ACCESS_ALLOWED_ACE_TYPE:  
      if (wszDomainName[0] == 0)  
      {  
        wprintf(L"\nPermissions granted to %s\n", wszAccName);  
      }  
      else wprintf(L"\nPermissions granted to %s\\%s\n", wszDomainName, wszAccName);  
      DisplayPermissions(pAce->Mask);  
      break;  
  
      case ACCESS_DENIED_ACE_TYPE:  
      if (wszDomainName[0] == 0)  
      {  
        wprintf(L"\nPermissions denied to %s\n", wszAccName);  
      }  
      else wprintf(L"\nPermissions denied to %s\\%s\n", wszDomainName, wszAccName);  
      DisplayPermissions(pAce->Mask);  
      break;  
  
      default:  
      wprintf(L"Unknown ACE Type");  
    }  
  }  
  
  // Free memory allocated for buffers.  
  delete [] wszAccName;  
  delete [] wszDomainName;  
  
  return MQ_OK;  
}  

To display the permissions contained in an access mask

  1. For each permission, compare the corresponding Message Queuing constant to the access mask passed to the function.

  2. If the bits of a permission are contained in the access mask, display the name of the permission.

HRESULT DisplayPermissions(  
                           ACCESS_MASK amMask  
                           )  
{  
  
  if ((amMask & MQSEC_QUEUE_GENERIC_ALL) == MQSEC_QUEUE_GENERIC_ALL)  
  {  
    wprintf(L"\tFull Control\n");  
  }  
  
  if ((amMask & MQSEC_DELETE_QUEUE) == MQSEC_DELETE_QUEUE)  
  {  
    wprintf(L"\tDelete\n");  
  }  
  
  if ((amMask & MQSEC_RECEIVE_MESSAGE) == MQSEC_RECEIVE_MESSAGE)  
  {  
    wprintf(L"\tReceive Message\n");  
  }  
  
  if ((amMask & MQSEC_DELETE_MESSAGE) == MQSEC_DELETE_MESSAGE)  
  {  
    wprintf(L"\tDelete Message\n");  
  }  
  
  if ((amMask & MQSEC_PEEK_MESSAGE) == MQSEC_PEEK_MESSAGE)  
  {  
    wprintf(L"\tPeek Message\n");  
  }  
  
  if ((amMask & MQSEC_RECEIVE_JOURNAL_MESSAGE) == MQSEC_RECEIVE_JOURNAL_MESSAGE)  
  {  
    wprintf(L"\tReceive Journal Message\n");  
  }  
  
  if ((amMask & MQSEC_DELETE_JOURNAL_MESSAGE) == MQSEC_DELETE_JOURNAL_MESSAGE)  
  {  
    wprintf(L"\tDelete Journal Message\n");  
  }  
  
  if ((amMask & MQSEC_GET_QUEUE_PROPERTIES) == MQSEC_GET_QUEUE_PROPERTIES)  
  {  
    wprintf(L"\tGet Properties\n");  
  }  
  
  if ((amMask & MQSEC_SET_QUEUE_PROPERTIES) == MQSEC_SET_QUEUE_PROPERTIES)  
  {  
    wprintf(L"\tSet Properties\n");  
  }  
  
  if ((amMask & MQSEC_GET_QUEUE_PERMISSIONS) == MQSEC_GET_QUEUE_PERMISSIONS)  
  {  
    wprintf(L"\tGet Permissions\n");  
  }  
  
  if ((amMask & MQSEC_CHANGE_QUEUE_PERMISSIONS) == MQSEC_CHANGE_QUEUE_PERMISSIONS)  
  {  
    wprintf(L"\tSet Permissions\n");  
  }  
  
  if ((amMask & MQSEC_TAKE_QUEUE_OWNERSHIP) == MQSEC_TAKE_QUEUE_OWNERSHIP)  
  {  
    wprintf(L"\tTake Ownership\n");  
  }  
  
  if ((amMask & MQSEC_WRITE_MESSAGE) == MQSEC_WRITE_MESSAGE)  
  {  
    wprintf(L"\tSend Message\n");  
  }  
  
  return S_OK;  
}