C-C++ Code Example: Creating a Security Descriptor

 

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 two application-defined functions that can be used to specify security settings in the security descriptor when a queue is created. More specifically, the example creates a security descriptor that gives the current user and a specified trusted user full control and allows members of the Everyone group to send messages to the queue. Then the security descriptor generated is used to create a queue with the path name specified by the user.

The first function, CreateQSecDescriptor, receives the account name of a trusted user and the path name of the queue to be created as well as a buffer for returning the format name of the queue and the length of this buffer as input parameters. In addition, CreateQSecDescriptor sets the security information for the discretionary access control list (DACL) in a SECURITY_DESCRIPTOR structure and calls the application-defined function CreateMSMQQueue from the C/C++ code example C/C++ Code Example: Creating a Queue to create a new queue with the path name supplied by the caller and the SECURITY_DESCRIPTOR structure created. The queue created can be a local private queue, or a local or remote public queue.

The second function, GetSid, which is called from CreateQSecDescriptor, receives the account name of the trusted user and a pointer that will store a pointer to the buffer for storing the trusted user's security identifier (SID), allocates memory for the buffer needed, and calls LookupAccountName to retrieve the trusted user's SID.

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 create a new queue with a custom DACL

  1. Validate the input parameters.

Note

That 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 retrieve the SIDs of the trustees and create a new security descriptor.

  2. Call OpenProcessToken to open the access token associated with the calling process.

  3. Call GetTokenInformation twice to retrieve information about the token user's account that includes its SID from the access token associated with the calling process. The first call is made with a null buffer to find out the size of the buffer needed. The second call, which is made with the TokenInformationClass parameter set to TokenUser after memory is allocated for the TOKEN_USER structure needed to store the information, retrieves the information about the token user's account.

  4. Call IsValidSid to verify that the SID obtained from the access token is valid.

  5. Call AllocateAndInitializeSid to obtain the SID of the Everyone group. In the parameters passed to AllocateAndInitializeSid, the number of subauthorities in the SID is set to 1, and the value of the first subauthority is set to SECURITY_WORLD_RID.

  6. Call the second function described in this example (GetSid) to retrieve the SID of the trusted user, passing the account name of the trusted user and the address of a pointer to a SID. On return, this pointer will point to the buffer allocated by GetSid for storing the trusted user's SID.

  7. Calculate the size of a buffer that can store a DACL containing three access control entries (ACEs) in the form of ACCESS_ALLOWED_ACE structures with the SIDs of the Everyone group, the current user, and the trusted user.

    This buffer must accommodate an ACL structure (the ACL header) followed by the three ACCESS_ALLOWED_ACE structures in contiguous memory. However, each ACCESS_ALLOWED_ACE structure is defined with a SidStart member that contains only the first DWORD of the SID, and contiguous memory must be allocated directly after the SidStart member to accommodate the remaining bytes of the SID. As a result, for each ACE, the size of an empty ACCESS_ALLOWED_ACE structure and the size of the respective SID less the size of a DWORD are added to the size the ACL structure.

  8. Allocate the memory for the DACL buffer and call InitializeAcl to initialize the ACL structure in it.

  9. Call AddAccessAllowedAce three times to add an access-allowed ACE for each of the three trustees with an access mask that contains the access rights for full control.

  10. Call InitializeSecurityDescriptor to initialize a SECURITY_DESCRIPTOR structure in the absolute format. An absolute SECURITY_DESCRIPTOR structure contains pointers to the security information associated with the queue. A self-relative SECURITY_DESCRIPTOR structure stores all the security information in a contiguous block of memory.

  11. Call SetSecurityDescriptorDacl to insert the modified DACL into the absolute SECURITY_DESCRIPTOR structure.

    This example sets only the DACL in the security descriptor. The default values are set for the other components of the queue's security information. The other components can also be set using the applicable security functions. For example, the following call to SetSecurityDescriptorOwner adds the trusted user to the SECURITY_DESCRIPTOR structure as the queue owner.

    if (SetSecurityDescriptorOwner(  
                                   &sd,  
                                   pTrustedUserSid,  
                                   FALSE  
                                   ) == FALSE)  
    {  
      //  
      // Handle error.  
      //  
    }  
    
  12. Call CreateMSMQQueue from the code example C/C++ Code Example: Creating a Queue to create a new queue with the path name supplied by the caller and the SECURITY_DESCRIPTOR structure created.

  13. Free the memory allocated for the TOKEN_USER structure, the SIDs of the Everyone group and the trusted user, and the DACL 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 CreateQSecDescriptor(  
                             LPCWSTR wszUserName,  
                             LPWSTR wszPathName,  
                             LPWSTR wszOutFormatName,  
                             DWORD * pdwOutFormatNameLength  
                             )  
{  
  
  // Validate the input parameter.  
  if (wszUserName == NULL || wszPathName == NULL || wszOutFormatName == NULL || pdwOutFormatNameLength == NULL)  
  {  
    return MQ_ERROR_INVALID_PARAMETER;  
  }  
  
  HANDLE hToken = NULL;  
  DWORD dwBufferSize = 0;  
  PTOKEN_USER pTokenUser = NULL;  
  PSID pEveryoneSid = NULL;  
  PSID pTrustedUserSid = NULL;  
  PACL pDacl = NULL;  
  DWORD cbDacl = 0;  
  SECURITY_DESCRIPTOR sd;  
  DWORD dwErrorCode = 0;  
  HRESULT hr = MQ_OK;  
  
  // Open the access token associated with the calling process.  
  if (OpenProcessToken(  
                       GetCurrentProcess(),  
                       TOKEN_QUERY,  
                       &hToken  
                       ) == FALSE)  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"OpenProcessToken failed. GetLastError returned: %d\n", dwErrorCode);  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
  // Retrieve the token information in a TOKEN_USER structure.  
  GetTokenInformation(  
                      hToken,  
                      TokenUser,      // Request for a TOKEN_USER structure.  
                      NULL,  
                      0,  
                      &dwBufferSize  
                      );  
  
  pTokenUser = (PTOKEN_USER) new BYTE[dwBufferSize];  
  memset(pTokenUser, 0, dwBufferSize);  
  if (GetTokenInformation(hToken,  
                          TokenUser,  
                          pTokenUser,  
                          dwBufferSize,  
                          &dwBufferSize  
                          ))  
  {  
    CloseHandle(hToken);  
  }  
  else  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"GetTokenInformation failed. GetLastError returned: %d\n", dwErrorCode);  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
  if (IsValidSid(pTokenUser->User.Sid) == FALSE)  
  {  
    wprintf(L"The owner SID is invalid.\n");  
    delete [] pTokenUser;  
    return MQ_ERROR;  
  }  
  
  // Retrieve the SID of the Everyone group.  
  SID_IDENTIFIER_AUTHORITY WorldAuth = SECURITY_WORLD_SID_AUTHORITY;  
  if (AllocateAndInitializeSid(  
                               &WorldAuth,          // Top-level SID authority  
                               1,                   // Number of subauthorities  
                               SECURITY_WORLD_RID,  // Subauthority value  
                               0,  
                               0,  
                               0,  
                               0,  
                               0,  
                               0,  
                               0,  
                               &pEveryoneSid        // SID returned as OUT parameter  
                               ) == FALSE)  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"AllocateAndInitializeSid failed. GetLastError returned: %d\n", dwErrorCode);  
    delete [] pTokenUser;  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
  // Retrieve the SID of the trusted user.  
  hr = GetSid(wszUserName, &pTrustedUserSid);  
  if (FAILED(hr))  
  {  
    wprintf(L"GetSid failed. Error code: 0x%X\n", hr);  
    delete [] pTokenUser;  
    FreeSid(pEveryoneSid);  
    return hr;  
  }  
  
  // Calculate the amount of memory that must be allocated for the DACL.  
  cbDacl = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)*3 - sizeof(DWORD)*3;  
  cbDacl += GetLengthSid(pTokenUser->User.Sid);  
  cbDacl += GetLengthSid(pEveryoneSid);  
  cbDacl += GetLengthSid(pTrustedUserSid);  
  
  // Create and initialize an ACL.  
  pDacl = (PACL) new BYTE[cbDacl];  
  if (pDacl == NULL)  
  {  
    delete [] pTokenUser;  
    FreeSid(pEveryoneSid);  
    FreeSid(pTrustedUserSid);  
    return MQ_ERROR_INSUFFICIENT_RESOURCES;  
  }  
  memset(pDacl, 0, cbDacl);  
  if (InitializeAcl(  
                    pDacl,  
                    cbDacl,  
                    ACL_REVISION  // Required constant  
                    ) == FALSE)  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"InitializeAcl failed. GetLastError returned: %d\n", dwErrorCode);  
    delete [] pTokenUser;  
    FreeSid(pEveryoneSid);  
    FreeSid(pTrustedUserSid);  
    delete [] pDacl;  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
  // Add access-allowed ACEs for the three trustees.  
  if (AddAccessAllowedAce(  
                          pDacl,                    // Pointer to the ACL.  
                          ACL_REVISION,             // Required constant  
                          MQSEC_QUEUE_GENERIC_ALL,  // Access mask  
                          pTokenUser->User.Sid      // Pointer to the trustee's SID  
                          ) == FALSE)  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"AddAccessAllowedAce failed for the queue owner. GetLastError returned: %d\n", dwErrorCode);  
    delete [] pTokenUser;  
    FreeSid(pEveryoneSid);  
    FreeSid(pTrustedUserSid);  
    delete [] pDacl;  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
  if (AddAccessAllowedAce(  
                          pDacl,                     // Pointer to the ACL  
                          ACL_REVISION,              // Required constant  
                          MQSEC_QUEUE_GENERIC_READ,  // Access mask  
                          pEveryoneSid               // Pointer to the trustee's SID  
                        ) == FALSE)  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"AddAccessAllowedAce failed for the Everyone group. GetLastError returned: %d\n", dwErrorCode);  
    delete [] pTokenUser;  
    FreeSid(pEveryoneSid);  
    FreeSid(pTrustedUserSid);  
    delete [] pDacl;  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
  if (AddAccessAllowedAce(  
                          pDacl,                    // Pointer to the ACL structure  
                          ACL_REVISION,             // Required constant  
                          MQSEC_QUEUE_GENERIC_ALL,  // Access mask  
                          pTrustedUserSid           // Pointer to the trustee's SID  
                        ) == FALSE)  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"AddAccessAllowedAce failed for the trusted owner. GetLastError returned: %d\n", dwErrorCode);  
    delete [] pTokenUser;  
    FreeSid(pEveryoneSid);  
    FreeSid(pTrustedUserSid);  
    delete [] pDacl;  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
  // Initialize an absolute SECURITY_DESCRIPTOR structure.  
  if (InitializeSecurityDescriptor(  
                                   &sd,  
                                   SECURITY_DESCRIPTOR_REVISION  // Required constant  
                                   ) == FALSE)  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"InitializeSecurityDescriptor failed. GetLastError returned: %d\n", dwErrorCode);  
    delete [] pTokenUser;  
    FreeSid(pEveryoneSid);  
    FreeSid(pTrustedUserSid);  
    delete [] pDacl;  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
  // Insert the DACL into the absolute SECURITY_DESCRIPTOR structure.  
  if (SetSecurityDescriptorDacl(  
                                &sd,  
                                TRUE,  
                                pDacl,  
                                FALSE  
                                ) == FALSE)  
  {  
    dwErrorCode = GetLastError();  
    wprintf(L"SetSecurityDescriptorDacl failed. GetLastError returned: %d\n", dwErrorCode);  
    delete [] pTokenUser;  
    FreeSid(pEveryoneSid);  
    FreeSid(pTrustedUserSid);  
    delete [] pDacl;  
    return HRESULT_FROM_WIN32(dwErrorCode);  
  }  
  
 // Create a queue using the absolute SECURITY_DESCRIPTOR structure.  
  hr = CreateMSMQQueue(  
                       wszPathName,  
                       &sd,  
                       wszOutFormatName,  
                       pdwOutFormatNameLength  
                       );  
  if (FAILED(hr))  
  {  
    wprintf(L"CreateMSMQQueue returned 0x%X\n", hr);  
  }  
  
  delete [] pTokenUser;  
  FreeSid(pEveryoneSid);  
  FreeSid(pTrustedUserSid);  
  delete [] pDacl;  
  return hr;  
}  

To retrieve the SID for a user account

  1. Validate the input parameters.

  2. Create buffers for the SID and the domain name that may be large enough.

  3. In a loop, call LookupAccountName to retrieve the SID for the account name supplied. If the buffer for the SID or the buffer for the domain name is not large enough, the buffer size needed is returned in cbSid or cchDomainName, respectively, and a new buffer is allocated before the next call to LookupAccountName. Note that the information is retrieved on the local system when the lpSystemName parameter is set to NULL.

  4. Free the memory allocated for the domain name buffer.

Code Example

HRESULT GetSid(  
               LPCWSTR wszAccName,  
               PSID * ppSid  
               )   
{  
  
  // Validate the input parameters.  
  if (wszAccName == NULL || ppSid == NULL)  
  {  
    return MQ_ERROR_INVALID_PARAMETER;  
  }  
  
  // Create buffers that may be large enough.  
  // If a buffer is too small, the count parameter will be set to the size needed.  
  const DWORD INITIAL_SIZE = 32;  
  DWORD cbSid = 0;  
  DWORD dwSidBufferSize = INITIAL_SIZE;  
  DWORD cchDomainName = 0;  
  DWORD dwDomainBufferSize = INITIAL_SIZE;  
  WCHAR * wszDomainName = NULL;  
  SID_NAME_USE eSidType;  
  DWORD dwErrorCode = 0;  
  HRESULT hr = MQ_OK;  
  
  // Create buffers for the SID and the domain name.  
  *ppSid = (PSID) new BYTE[dwSidBufferSize];  
  if (*ppSid == NULL)  
  {  
    return MQ_ERROR_INSUFFICIENT_RESOURCES;  
  }  
  memset(*ppSid, 0, dwSidBufferSize);  
  wszDomainName = new WCHAR[dwDomainBufferSize];  
  if (wszDomainName == NULL)  
  {  
    return MQ_ERROR_INSUFFICIENT_RESOURCES;  
  }  
  memset(wszDomainName, 0, dwDomainBufferSize*sizeof(WCHAR));  
  
  // Obtain the SID for the account name passed.  
  for ( ; ; )  
  {  
  
    // Set the count variables to the buffer sizes and retrieve the SID.  
    cbSid = dwSidBufferSize;  
    cchDomainName = dwDomainBufferSize;  
    if (LookupAccountNameW(  
                           NULL,            // Computer name. NULL for the local computer  
                           wszAccName,  
                           *ppSid,          // Pointer to the SID buffer. Use NULL to get the size needed,  
                           &cbSid,          // Size of the SID buffer needed.  
                           wszDomainName,   // wszDomainName,  
                           &cchDomainName,  
                           &eSidType  
                           ))  
    {  
      if (IsValidSid(*ppSid) == FALSE)  
      {  
         wprintf(L"The SID for %s is invalid.\n", wszAccName);  
         dwErrorCode = MQ_ERROR;  
      }  
      break;  
    }  
    dwErrorCode = GetLastError();  
  
    // Check if one of the buffers was too small.  
    if (dwErrorCode == ERROR_INSUFFICIENT_BUFFER)  
    {  
      if (cbSid > dwSidBufferSize)  
      {  
  
        // Reallocate memory for the SID buffer.  
        wprintf(L"The SID buffer was too small. It will be reallocated.\n");  
        FreeSid(*ppSid);  
        *ppSid = (PSID) new BYTE[cbSid];  
        if (*ppSid == NULL)  
        {  
          return MQ_ERROR_INSUFFICIENT_RESOURCES;  
        }  
        memset(*ppSid, 0, cbSid);  
        dwSidBufferSize = cbSid;  
      }  
      if (cchDomainName > dwDomainBufferSize)  
      {  
  
        // Reallocate memory for the domain name buffer.  
        wprintf(L"The domain name buffer was too small. It will be reallocated.\n");  
        delete [] wszDomainName;  
        wszDomainName = new WCHAR[cchDomainName];  
        if (wszDomainName == NULL)  
        {  
          return MQ_ERROR_INSUFFICIENT_RESOURCES;  
        }  
        memset(wszDomainName, 0, cchDomainName*sizeof(WCHAR));  
        dwDomainBufferSize = cchDomainName;  
      }  
    }  
    else  
    {  
      wprintf(L"LookupAccountNameW failed. GetLastError returned: %d\n", dwErrorCode);  
      hr = HRESULT_FROM_WIN32(dwErrorCode);  
      break;  
    }  
  }  
  
  delete [] wszDomainName;  
  return hr;   
}