Microsoft KB Archive/102102

From BetaArchive Wiki
< Microsoft KB Archive
Revision as of 11:24, 21 July 2020 by X010 (talk | contribs) (Text replacement - "&" to "&")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

HOWTO: Add an Access-Allowed ACE to a File

Q102102



The information in this article applies to:


  • Microsoft Win32 Application Programming Interface (API), used with:
    • Microsoft Windows NT Server versions 3.5, 3.51, 4.0
    • Microsoft Windows NT Workstation versions 3.5, 3.51, 4.0
    • Microsoft Windows 2000 Server
    • Microsoft Windows 2000 Professional





SUMMARY

This article demonstrates how to add an "access-allowed" access control entry (ACE) to a file.



MORE INFORMATION

When an access-allowed ACE is added to a file's discretionary access control list (DACL), particular user or group is provided access to the file. In most cases, the file's DACL is not large enough to add an additional ACE. Therefore, it is necessary to create a new access control list (ACL) and copy the ACEs from the file's existing DACL to it. Once the existing ACEs have been copied to the new ACL, the new ACE should be added. Then the new ACL can replace the DACL in the file's security descriptor (SD). This process is explained in more detail in the following sample code:

Sample Code

The following sample code demonstrates the basic steps that are required to add an access-allowed ACE to a file's DACL. Steps 1-17 in the comments of the code are discussed in more detail at the end of this article.

#include <windows.h>
#include <stdio.h>

#define heapalloc(x) (HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, x))
#define heapfree(x)  (HeapFree(GetProcessHeap(), 0, x))

BOOL AddAccessRights(CHAR *lpszFileName, DWORD dwAccessMask) {

   // SID variables.
   SID_NAME_USE   snuType;
   char *         szDomain       = NULL;
   DWORD          cbDomain       = 0;
   LPVOID         pUserSID       = NULL;
   DWORD          cbUserSID      = 0;

   // User name variables.
   char *         szUserName     = NULL;
   DWORD          cbUserName     = 0;

   // File SD variables.
   PSECURITY_DESCRIPTOR pFileSD  = NULL;
   DWORD          cbFileSD       = 0;

   // New SD variables.
   PSECURITY_DESCRIPTOR pNewSD   = NULL;

   // ACL variables.
   PACL           pACL           = NULL;
   BOOL           fDaclPresent;
   BOOL           fDaclDefaulted;
   ACL_SIZE_INFORMATION AclInfo;

   // New ACL variables.
   PACL           pNewACL        = NULL;
   DWORD          cbNewACL       = 0;

   // Temporary ACE.
   LPVOID         pTempAce       = NULL;
   UINT           CurrentAceIndex;

   // Assume function will fail.
   BOOL           fResult        = FALSE;
   BOOL           fAPISuccess;

   __try {

      // 
      // STEP 1: Get the logged on user name.
      // 
      fAPISuccess = GetUserName(szUserName, &cbUserName);

      // API should have failed with insufficient buffer.
      if (fAPISuccess)
         __leave;
      else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
         printf("GetUserName() failed.  Error %d\n", GetLastError());
         __leave;
      }

      szUserName = (char *) heapalloc(cbUserName);
      if (!szUserName) {
         printf("HeapAlloc() failed.  Error %d\n", GetLastError());
         __leave;
      }

      fAPISuccess = GetUserName(szUserName, &cbUserName);
      if (!fAPISuccess) {
         printf("GetUserName() failed.  Error %d\n", GetLastError());
         __leave;
      }

      // 
      // STEP 2: Get SID for current user.
      // 
      fAPISuccess = LookupAccountName((LPSTR) NULL, szUserName,
            pUserSID, &cbUserSID, szDomain, &cbDomain, &snuType);

      // API should have failed with insufficient buffer.
      if (fAPISuccess)
         __leave;
      else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
         printf("LookupAccountName() failed.  Error %d\n", 
               GetLastError());
         __leave;
      }

      pUserSID = heapalloc(cbUserSID);
      if (!pUserSID) {
         printf("HeapAlloc() failed.  Error %d\n", GetLastError());
         __leave;
      }

      szDomain = (char *) heapalloc(cbDomain);
      if (!szDomain) {
         printf("HeapAlloc() failed.  Error %d\n", GetLastError());
         __leave;
      }

      fAPISuccess = LookupAccountName((LPSTR) NULL, szUserName,
            pUserSID, &cbUserSID, szDomain, &cbDomain, &snuType);
      if (!fAPISuccess) {
         printf("LookupAccountName() failed.  Error %d\n", 
               GetLastError());
         __leave;
      }

      // 
      // STEP 3: Get security descriptor (SD) for file.
      // 
      fAPISuccess = GetFileSecurity(lpszFileName, 
            DACL_SECURITY_INFORMATION, pFileSD, 0, &cbFileSD);

      // API should have failed with insufficient buffer.
      if (fAPISuccess)
         __leave;
      else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
         printf("GetFileSecurity() failed.  Error %d\n", 
               GetLastError());
         __leave;
      }

      pFileSD = heapalloc(cbFileSD);
      if (!pFileSD) {
         printf("HeapAlloc() failed.  Error %d\n", GetLastError());
         __leave;
      }

      fAPISuccess = GetFileSecurity(lpszFileName, 
            DACL_SECURITY_INFORMATION, pFileSD, cbFileSD, &cbFileSD);
      if (!fAPISuccess) {
         printf("GetFileSecurity() failed.  Error %d\n", 
               GetLastError());
         __leave;
      }

      // 
      // STEP 4: Initialize new SD.
      // 
      pNewSD = heapalloc(cbFileSD); // Should be same size as FileSD.
      if (!pNewSD) {
         printf("HeapAlloc() failed.  Error %d\n", GetLastError());
         __leave;
      }

      if (!InitializeSecurityDescriptor(pNewSD, 
            SECURITY_DESCRIPTOR_REVISION)) {
         printf("InitializeSecurityDescriptor() failed.  Error %d\n", 
               GetLastError());
         __leave;
      }

      // 
      // STEP 5: Get DACL from SD.
      // 
      if (!GetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,
            &fDaclDefaulted)) {
         printf("GetSecurityDescriptorDacl() failed.  Error %d\n", 
               GetLastError());
         __leave;
      }

      // 
      // STEP 6: Get size information for DACL.
      // 
      AclInfo.AceCount = 0; // Assume NULL DACL.
      AclInfo.AclBytesFree = 0;
      AclInfo.AclBytesInUse = sizeof(ACL);

      // If not NULL DACL, gather size information from DACL.
      if (fDaclPresent && pACL) {    
         
         if(!GetAclInformation(pACL, &AclInfo, 
               sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) {
            printf("GetAclInformation() failed.  Error %d\n",
               GetLastError());
            __leave;
         }
      }

      // 
      // STEP 7: Compute size needed for the new ACL.
      // 
      cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) 
            + GetLengthSid(pUserSID) - sizeof(DWORD);

      // 
      // STEP 8: Allocate memory for new ACL.
      // 
      pNewACL = (PACL) heapalloc(cbNewACL);
      if (!pNewACL) {
         printf("HeapAlloc() failed.  Error %d\n", GetLastError());
         __leave;
      }

      // 
      // STEP 9: Initialize the new ACL.
      // 
      if(!InitializeAcl(pNewACL, cbNewACL, ACL_REVISION2)) {
         printf("InitializeAcl() failed.  Error %d\n", GetLastError());
         __leave;
      }

      // 
      // STEP 10: If DACL is present, copy it to a new DACL.
      // 
      if (fDaclPresent) {

         // 
         // STEP 11: Copy the file's ACEs to the new ACL.
         // 
         if (AclInfo.AceCount) {

            for (CurrentAceIndex = 0; 
                  CurrentAceIndex < AclInfo.AceCount;
                  CurrentAceIndex++) {

               // 
               // STEP 12: Get an ACE.
               // 
               if(!GetAce(pACL, CurrentAceIndex, &pTempAce)) {
                  printf("GetAce() failed.  Error %d\n", 
                        GetLastError());
                  __leave;
               }

               // 
               // STEP 13: Add the ACE to the new ACL.
               // 
               if(!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
                     ((PACE_HEADER) pTempAce)->AceSize)) {
                  printf("AddAce() failed.  Error %d\n", 
                        GetLastError());
                  __leave;
               }
            }
         }
      }

      // 
      // STEP 14: Add the access-allowed ACE to the new DACL.
      // 
      if (!AddAccessAllowedAce(pNewACL, ACL_REVISION2, dwAccessMask,
            pUserSID)) {
         printf("AddAccessAllowedAce() failed.  Error %d\n",
               GetLastError());
         __leave;
      }

      // 
      // STEP 15: Set the new DACL to the file SD.
      // 
      if (!SetSecurityDescriptorDacl(pNewSD, TRUE, pNewACL, 
            FALSE)) {
         printf("() failed.  Error %d\n", GetLastError());
         __leave;
      }

      // 
      // STEP 16: Set the SD to the File.
      // 
      if (!SetFileSecurity(lpszFileName, DACL_SECURITY_INFORMATION,
            pNewSD)) {
         printf("SetFileSecurity() failed.  Error %d\n", 
               GetLastError());
         __leave;
      }

      fResult = TRUE;

   } __finally {

      // 
      // STEP 17: Free allocated memory
      // 
      if (szUserName)
         heapfree(szUserName);
      
      if (pUserSID)
         heapfree(pUserSID);

      if (szDomain)
         heapfree(szDomain);

      if (pFileSD)
         heapfree(pFileSD);

      if (pNewSD)
         heapfree(pNewSD);

      if (pNewACL)
         heapfree(pNewACL);
   }
   
   return fResult;
}

int main(int argc, char *argv[]) {

   if (argc == 1) {
      printf("usage: \"%s\" <filename>\n", argv[0]);
      return 1;
   }

   if (AddAccessRights(argv[1], GENERIC_ALL | STANDARD_RIGHTS_ALL))
      return 1;
   else {
      printf("AddAccessRights() succeeded.\n");
      return 0;
   }

} 

Explanation of Steps in Sample Code

  1. The GetUserName function is called to retrieve the name of the user who is currently logged on. The function is actually called twice: once to obtain the required buffer size, and then again to retrieve the user name.
  2. The LookupAccountName function is called to obtain the security identifier (SID) for the user name that is returned by the GetUserName function in step 1. Note that the LookupAccountName function returns the SID of the first user or group that matches the specified name. This SID is used later in a call to the AddAccessAllowedACE function. The LookupAccountName function also provides the user's domain.


The LookupAccountName function is actually called twice: once to determine the required buffer sizes, and then again to retrieve the account information.

NOTE: The LookupAccountName function can be a fairly expensive call because multiple SAM databases may need to be queried to retrieve the desired information. An alternative method to retrieve the SID is to look in the process token.

  1. The GetFileSecurity function is used to obtain a copy of the file's security descriptor (SD). The GetFileSecurity function is actually called twice: once to determine the required buffer size, and then again to retrieve the SD.


NOTE: Security descriptors have two possible formats: self-relative and absolute. The GetFileSecurity function returns an SD in self-relative format, but the SetFileSecurity function expects absolute format. This is one reason that the code must create a new SD and copy the information, instead of simply modifying the SD from the GetFileSecurity function and passing it to the SetFileSecurity function. It is possible to call the MakeAbsoluteSD function to do the conversion, but there may not be enough room in the current ACL, as mentioned.

  1. Because the SetFileSecurity function requires the DACL to be contained in an SD, initialize a new SD by calling the InitializeSecurityDescriptor function.
  2. The GetSecurityDescriptorDacl function retrieves a pointer to the DACL in the SD.
  3. The GetAclInformation function is called to obtain size information on the file's DACL in the form of an ACL_SIZE_INFORMATION structure. This information is used when computing the size of the new DACL and when copying ACEs.
  4. Compute the exact number of bytes to allocate for the new DACL by using the information just obtained from the GetAclInformation function. The AclBytesInUse member of the ACL_SIZE_INFORMATION structure represents the number of bytes that are being used in the file's DACL. Add this number to the size of an ACCESS_ALLOWED_ACE and the size of the user's SID. Then subtract the size of a DWORD to obtain the exact number of bytes for the DACL.
  5. Allocate memory for the new ACL that ultimately contains the file's existing ACEs plus the access-allowed ACE.
  6. Initialize the ACL structure.
  7. Check the flag that is returned by the GetSecurityDescriptorDacl function to determine whether a DACL was present in the file's SD. If a DACL was not present, then skip the code that copies the file's ACEs to the new DACL.
  8. The AceCount member of the ACL_SIZE_INFORMATION structure contains the number of ACEs in the DACL. Loop once for each ACE.
  9. Retrieve a pointer to the ACE in the file's existing DACL.
  10. Copy the ACE to the new ACL. Pass MAXDWORD for the dwStartingAceIndex parameter of the AddAce function to ensure that the ACE is added to the end of the DACL. The AceSize member of the ACE_HEADER provides the size of the ACE.
  11. After copying all of the file's original ACEs to the new ACL, add an access-allowed ACE, based on the value of dwAccessMask.
  12. The SetFileSecurity function takes a pointer to a security descriptor. For this reason, it is necessary to attach the new DACL to a temporary SD by using the SetSecurityDescriptorDacl function.
  13. Set the DACL to the file's SD by calling the SetFileSecurity function. The DACL_SECURITY_INFORMATION flag causes the DACL in the new SD to be applied to the file's SD. Only the file's DACL is set. The other security information in the file's SD remains unchanged.
  14. Free the memory that was allocated for the new DACL.

Access-Denied ACEs

Please note that a similar process can be used to add an access-denied ACE to a file's DACL. An access-denied ACE is used to deny a specific user or group access to a file. Access-denied ACEs should appear before access-allowed ACEs in an ACL. Therefore, when adapting the sample code to add an access-denied ACE, the call to the AddAccessDeniedAce function should precede the code that copies the existing ACEs to the new ACL.

Securing Other Objects

Access can be granted to objects other than files by substituting the GetFileSecurity function call with a GetKernelObjectSecurity, GetUserObjectSecurity, or GetPrivateObjectSecurity function call.

Additional query words:

Keywords : kbAccCtrl kbAPI kbKernBase kbOSWin2000 kbSecurity kbFAQ kbDSupport kbGrpDSKernBase
Issue type : kbhowto
Technology : kbAudDeveloper kbWin32sSearch kbWin32API


Last Reviewed: October 21, 2000
© 2001 Microsoft Corporation. All rights reserved. Terms of Use.