首页  编辑  

ACE API使用

Tags: /超级猛料/API.Windows应用程序接口/其他相关/   Date Created:

寄件者:Filip Poverud (filip.poverud@gjensidige.no)

主旨:Working with ACEs and ACLs.

新闻群组:microsoft.public.win32.programmer.kernel

日期:2001-12-12 02:05:06 PST

Working with ACEs and ACLs.

There are some steps you need to follow in order for the

Security Descriptor to be correct :

- Create the Identifier Authority Structure

- Open your object with WRITE_DAC access

- Allocate and initialize the SID with the corresponding

RID to step 1

- Initialize the ACL

- Add ACE with your Access Mask (Is the error here ?)

- Initiaize the security descriptor

- Set the DACL for the security descriptor

- Set the security for your object

---

Each step has its own corresponding function.

Example for Registry Keys in Delphi 5.0 code :

This example set Everyone with Read and System with Full

access to a given key. The code should not be difficult to

convert to C++ or VB. Remark that I set one type of access

for each SID. Also ... in order to avoid conflicts I reset

the security via. AllowRegKeyToEveryone();

Don't mind the Delphi specific declarations, some of this

types and functions are not available in default units.

***CODE START HERE***

program RegMod;

{$APPTYPE CONSOLE}

uses

 SysUtils,

 Registry,

 Windows,

 Dialogs;

const

 SECURITY_NULL_SID_AUTHORITY = 0;

 SECURITY_WORLD_SID_AUTHORITY = 1;

 SECURITY_LOCAL_SID_AUTHORITY = 2;

 SECURITY_CREATOR_SID_AUTHORITY = 3;

 SECURITY_NT_AUTHORITY = 5;

 SECURITY_LOCAL_SYSTEM_RID = $00000012;

 SECURITY_WORLD_RID = $00000000;

 ACL_REVISION = 2;

 SECURITY_DESCRIPTOR_REVISION = 1;

type

 PACE_Header = ^TACE_Header;

 TACE_Header = record

   AceType: BYTE;

   AceFlags: BYTE;

   AceSize: WORD;

 end;

 PAccess_Allowed_ACE = ^TAccess_Allowed_ACE;

 TAccess_Allowed_ACE = record

   Header: TACE_Header;

   Mask: ACCESS_MASK;

   SidStart: DWORD;

 end;

type

 SE_OBJECT_TYPE = (

   SE_UNKNOWN_OBJECT_TYPE,

   SE_FILE_OBJECT,

   SE_SERVICE,

   SE_PRINTER,

   SE_REGISTRY_KEY,

   SE_LMSHARE,

   SE_KERNEL_OBJECT,

   SE_WINDOW_OBJECT,

   SE_DS_OBJECT,

   SE_DS_OBJECT_ALL,

   SE_PROVIDER_DEFINED_OBJECT,

   SE_WMIGUID_OBJECT

 );

 PPSID = ^PSID;

function SetPermissions : Boolean;

var

 sia,

 eia : TSIDIdentifierAuthority;

 pSystemSID,

 pEveryoneSID : PSID;

 sd : Windows.TSecurityDescriptor;

 pDacl : PACL;

 dwAclSize : DWORD;

 aHKey : HKEY;

 lRetCode : LongInt;

 bSuccess : Boolean;

begin

 sia.Value[0] := 0;

 sia.Value[1] := 0;

 sia.Value[2] := 0;

 sia.Value[3] := 0;

 sia.Value[4] := 0;

 sia.Value[5] := SECURITY_NT_AUTHORITY;

 pSystemSID := nil;

 eia.Value[0] := 0;

 eia.Value[1] := 0;

 eia.Value[2] := 0;

 eia.Value[3] := 0;

 eia.Value[4] := 0;

 eia.Value[5] := SECURITY_WORLD_SID_AUTHORITY;

 pEveryoneSID := nil;

 pDacl := nil;

 bSuccess := False;

 lRetCode := RegOpenKeyEx(

   HKEY_LOCAL_MACHINE,

   'SYSTEM\CurrentControlSet\Enum\Root\',

   0,

   WRITE_DAC,

   aHKey

   );

 if(lRetCode <> ERROR_SUCCESS) then begin

   WriteLn('Error in RegOpenKeyEx');

   result := false;

 end

 else

  WriteLn('Key successfully opened with WRITE_DAC

access.');

 if( not AllocateAndInitializeSID(

   sia,

   1,

   SECURITY_LOCAL_SYSTEM_RID,

   0, 0, 0, 0, 0, 0, 0,

   pSystemSid

   )) then

 begin

   WriteLn('Error in: AllocateAndInitializeSid');

 end

 else

   WriteLn('SYSTEM SID succesfully initialized.');

 if( not AllocateAndInitializeSID(

   eia,

   1,

   SECURITY_WORLD_RID,

   0, 0, 0, 0, 0, 0, 0,

   pEveryoneSid

   )) then

 begin

   WriteLn('Error in: AllocateAndInitializeSid');

 end

 else

  WriteLn('Everyone SID succesfully initialized.');

 dwAclSize := sizeof(TACL) +

              2 * ( sizeof(TAccess_Allowed_ACE) - sizeof

(DWORD) ) +

              GetLengthSid(pSystemSid) +

              GetLengthSid(pEveryoneSid);

 pDacl := PACL(HeapAlloc(GetProcessHeap(), 0, dwAclSize));

   if( not InitializeAcl(pDacl^,

     dwAclSize,

     ACL_REVISION)) then

   begin

     WriteLn('Error in: InitializeAcl');

   end

   else

     WriteLn('ACL succesfully initialized.');

   if(not AddAccessAllowedAce(

     pDacl^,

     ACL_REVISION,

     KEY_READ,

     pEveryoneSid

     )) then begin

       WriteLn('Error in: AddAccessAllowedAce.

[pEveryoneSid]');

   end

   else

    WriteLn('Access for Everyone succesfully added.');

   if(not AddAccessAllowedAce(

     pDacl^,

     ACL_REVISION,

     KEY_ALL_ACCESS,

     pSystemSid

     )) then begin

       WriteLn('Error in: AddAccessAllowedAce.

[pSystemSid]');

   end

   else

    WriteLn('Access for System succesfully added.');

   if(not InitializeSecurityDescriptor(@sd,

SECURITY_DESCRIPTOR_REVISION)) then begin

     WriteLn('Error in: InitializeSecurityDescriptor');

   end

   else

    WriteLn('Security Descriptor succesfully

initialized.');

   if(not SetSecurityDescriptorDacl(@sd, TRUE, pDacl,

FALSE)) then begin

     WriteLn('Error in: SetSecurityDescriptorDacl');

   end

   else

    WriteLn('DACL succesfully updated.');

   lRetCode := RegSetKeySecurity(

     aHKey,

     SECURITY_INFORMATION(DACL_SECURITY_INFORMATION),

     @sd

     );

   if(lRetCode <> ERROR_SUCCESS) then begin

     WriteLn('Error in: RegSetKeySecurity');

   end

   else

    WriteLn('Registry Key Security Operation

succesfull.');

   bSuccess := TRUE;

end;

function SetNamedSecurityInfoW(pObjectName : PWideChar;

                              ObjectType : SE_OBJECT_TYPE;

                              SecurityInfo :

SECURITY_INFORMATION;

                              ppSIDOwner,

                              ppSIDGroup : PPSID;

                              ppDacl,

                              ppSacl : PACL) : DWORD;

                              stdcall;

                              external 'advapi32.dll'

                             

name 'SetNamedSecurityInfoW';

function AllowRegKeyForEveryone(Key : HKEY; Path :

string) : Boolean;

var

 WidePath : PWideChar;

 Len      : Integer;

begin

 case Key of

   HKEY_LOCAL_MACHINE:

     Path := 'MACHINE\' + Path;

   HKEY_CURRENT_USER:

     Path := 'CURRENT_USER\' + Path;

   HKEY_CLASSES_ROOT:

     Path := 'CLASSES_ROOT\' + Path;

   HKEY_USERS:

     Path := 'USERS\' + Path;

 end;

 Len := (Length(Path) + 1) * SizeOf(WideChar);

 GetMem(WidePath,Len);

 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PChar

(Path), - 1, WidePath, Len);

 Result := SetNamedSecurityInfoW(WidePath,

SE_REGISTRY_KEY,DACL_SECURITY_INFORMATION, nil, nil, nil,

nil) = ERROR_SUCCESS;

 FreeMem(WidePath);

end;

var

 Result : Boolean;

begin

 with TRegistry.Create do

  begin

  try

    Result := AllowRegKeyForEveryOne

(HKEY_LOCAL_MACHINE,'SYSTEM\CurrentControlSet\Enum\Root\');

   if Result = False then

     begin

       WriteLn('FAILURE ! ... Press any key to

continue.');

       ReadLn;

     end;

   Access := KEY_ALL_ACCESS;

   RootKey := HKEY_CLASSES_ROOT;

   OpenKey('\*\shellex\ContextMenuHandlers\{969223C0-26AA-

11D0-90EE-444553540000}',True);

   CloseKey;

   RootKey := HKEY_LOCAL_MACHINE;

   CreateKey

('\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_PGPMEMLOCK');

   OpenKey

('\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_PGPMEMLOCK',Tr

ue);

     WriteInteger('NextInstance',1);

   CloseKey;

   OpenKey

('\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_PGPMEMLOCK\000

0',True);

     WriteString('BaseDevicaPath','HTREE\ROOT\0');

     WriteString('Class','Unknown');

     WriteString('ClassGUID','{4D36E97E-E325-11CE-BFC1-

08002BE10318}');

     WriteString('DeviceDesc','PGPmemlock');

     WriteString('Service','PGPmemlock');

     WriteInteger('FoundAtEnum',1);

     WriteInteger('Problem',0);

     WriteInteger('StatusFlags',8);

   CloseKey;

   OpenKey

('\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_PGPMEMLOCK\000

0\Control',True);

     WriteString('ActiveService','PGPmemlock');

   CloseKey;

   finally

     Free;

     Result := SetPermissions;

   if Result = False then

   begin

     WriteLn('FAILURE ! ... Press any key to continue.');

     ReadLn;

   end;

   end;

 end;

end.

***CODE END HERE***

---------------------------------------

HOWTO: Add an Access-Allowed ACE to a File

The information in this article applies to:

Microsoft Win32 Application Programming Interface (API), when used with:

the operating system: Microsoft Windows NT 4.0

the operating system: Microsoft Windows 2000

the operating system: Microsoft Windows XP

This article was previously published under Q102102

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), the corresponding user or group account associated with the ACE will be provided with the allowed access to the file by the system. 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 in the preferred order. The new DACL can then replace the old DACL in the file's security descriptor (SD). This process is explained in more detail in the sample code below:

For Windows NT versions 4.0 and earlier, the preferred order of ACEs is simple. In a DACL, all access-denied ACEs should precede any access-allowed ACEs. For Windows 2000 or later, the proper order of ACEs is more complicated because of the introduction of object-specific ACEs and automatic inheritance.

The following describes the preferred order for Windows 2000 or later:

To ensure that non-inherited ACEs have precedence over inherited ACEs, place all non-inherited ACEs in a group before any inherited ACEs. This ordering ensures, for example, that a non-inherited access-denied ACE is enforced regardless of any inherited ACE that allows access.

Within the groups of non-inherited ACEs and inherited ACEs, order ACEs according to ACE type, as the following shows:

Access-denied ACEs that apply to the object itself.

Access-denied ACEs that apply to a subobject of the object, such as a property set or property.

Access-allowed ACEs that apply to the object itself

Access-allowed ACEs that apply to a subobject of the object

The low-level access control APIs for adding ACEs to a DACL do not enforce the preferred order. The AddAce function adds ACEs at a specified location in an ACL. The AddAccessAllowedAce function adds an ACE to the end of an ACL. So, it is the caller's responsibility to ensure that the ACEs are added in the preferred order. The Order of ACEs in a DACL are documented in the following MSDN link:

http://msdn.microsoft.com/library/en-us/security/accctrl_2hik.asp?frame=true

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-21 in the comments of the code are discussed in more detail at the end of this article. #include <windows.h>

#include <tchar.h>

#include <stdio.h>

#define myheapalloc(x) (HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, x))

#define myheapfree(x)  (HeapFree(GetProcessHeap(), 0, x))

typedef BOOL (WINAPI *SetSecurityDescriptorControlFnPtr)(

  IN PSECURITY_DESCRIPTOR pSecurityDescriptor,

  IN SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest,

  IN SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet);

BOOL AddAccessRights(TCHAR *lpszFileName, TCHAR *lpszAccountName,

     DWORD dwAccessMask) {

  // SID variables.

  SID_NAME_USE   snuType;

  TCHAR *        szDomain       = NULL;

  DWORD          cbDomain       = 0;

  LPVOID         pUserSID       = NULL;

  DWORD          cbUserSID      = 0;

  // File SD variables.

  PSECURITY_DESCRIPTOR pFileSD  = NULL;

  DWORD          cbFileSD       = 0;

  // New SD variables.

  SECURITY_DESCRIPTOR  newSD;

  // 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 = 0;

  UINT           newAceIndex = 0;

  // Assume function will fail.

  BOOL           fResult        = FALSE;

  BOOL           fAPISuccess;

  SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;

  // New APIs available only in Windows 2000 and above for setting

  // SD control

  SetSecurityDescriptorControlFnPtr _SetSecurityDescriptorControl = NULL;

  __try {

     //

     // STEP 1: Get SID of the account name specified.

     //

     fAPISuccess = LookupAccountName(NULL, lpszAccountName,

           pUserSID, &cbUserSID, szDomain, &cbDomain, &snuType);

     // API should have failed with insufficient buffer.

     if (fAPISuccess)

        __leave;

     else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {

        _tprintf(TEXT("LookupAccountName() failed. Error %d\n"),

              GetLastError());

        __leave;

     }

     pUserSID = myheapalloc(cbUserSID);

     if (!pUserSID) {

        _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError());

        __leave;

     }

     szDomain = (TCHAR *) myheapalloc(cbDomain * sizeof(TCHAR));

     if (!szDomain) {

        _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError());

        __leave;

     }

     fAPISuccess = LookupAccountName(NULL, lpszAccountName,

           pUserSID, &cbUserSID, szDomain, &cbDomain, &snuType);

     if (!fAPISuccess) {

        _tprintf(TEXT("LookupAccountName() failed. Error %d\n"),

              GetLastError());

        __leave;

     }

     //

     // STEP 2: Get security descriptor (SD) of the file specified.

     //

     fAPISuccess = GetFileSecurity(lpszFileName,

           secInfo, pFileSD, 0, &cbFileSD);

     // API should have failed with insufficient buffer.

     if (fAPISuccess)

        __leave;

     else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {

        _tprintf(TEXT("GetFileSecurity() failed. Error %d\n"),

              GetLastError());

        __leave;

     }

     pFileSD = myheapalloc(cbFileSD);

     if (!pFileSD) {

        _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError());

        __leave;

     }

     fAPISuccess = GetFileSecurity(lpszFileName,

           secInfo, pFileSD, cbFileSD, &cbFileSD);

     if (!fAPISuccess) {

        _tprintf(TEXT("GetFileSecurity() failed. Error %d\n"),

              GetLastError());

        __leave;

     }

     //

     // STEP 3: Initialize new SD.

     //

     if (!InitializeSecurityDescriptor(&newSD,

           SECURITY_DESCRIPTOR_REVISION)) {

        _tprintf(TEXT("InitializeSecurityDescriptor() failed.")

           TEXT("Error %d\n"), GetLastError());

        __leave;

     }

     //

     // STEP 4: Get DACL from the old SD.

     //

     if (!GetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,

           &fDaclDefaulted)) {

        _tprintf(TEXT("GetSecurityDescriptorDacl() failed. Error %d\n"),

              GetLastError());

        __leave;

     }

     //

     // STEP 5: Get size information for DACL.

     //

     AclInfo.AceCount = 0; // Assume NULL DACL.

     AclInfo.AclBytesFree = 0;

     AclInfo.AclBytesInUse = sizeof(ACL);

     if (pACL == NULL)

        fDaclPresent = FALSE;

     // If not NULL DACL, gather size information from DACL.

     if (fDaclPresent) {    

       

        if (!GetAclInformation(pACL, &AclInfo,

              sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) {

           _tprintf(TEXT("GetAclInformation() failed. Error %d\n"),

                 GetLastError());

           __leave;

        }

     }

     //

     // STEP 6: Compute size needed for the new ACL.

     //

     cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE)

           + GetLengthSid(pUserSID) - sizeof(DWORD);

     //

     // STEP 7: Allocate memory for new ACL.

     //

     pNewACL = (PACL) myheapalloc(cbNewACL);

     if (!pNewACL) {

        _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError());

        __leave;

     }

     //

     // STEP 8: Initialize the new ACL.

     //

     if (!InitializeAcl(pNewACL, cbNewACL, ACL_REVISION2)) {

        _tprintf(TEXT("InitializeAcl() failed. Error %d\n"),

              GetLastError());

        __leave;

     }

     //

     // STEP 9 If DACL is present, copy all the ACEs from the old DACL

     // to the new DACL.

     //

     // The following code assumes that the old DACL is

     // already in Windows 2000 preferred order.  To conform

     // to the new Windows 2000 preferred order, first we will

     // copy all non-inherited ACEs from the old DACL to the

     // new DACL, irrespective of the ACE type.

     //

     

     newAceIndex = 0;

     if (fDaclPresent && AclInfo.AceCount) {

        for (CurrentAceIndex = 0;

              CurrentAceIndex < AclInfo.AceCount;

              CurrentAceIndex++) {

           //

           // STEP 10: Get an ACE.

           //

           if (!GetAce(pACL, CurrentAceIndex, &pTempAce)) {

              _tprintf(TEXT("GetAce() failed. Error %d\n"),

                    GetLastError());

              __leave;

           }

           //

           // STEP 11: Check if it is a non-inherited ACE.

           // If it is an inherited ACE, break from the loop so

           // that the new access allowed non-inherited ACE can

           // be added in the correct position, immediately after

           // all non-inherited ACEs.

           //

           if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags

              & INHERITED_ACE)

              break;

           //

           // STEP 12: Skip adding the ACE, if the SID matches

           // with the account specified, as we are going to

           // add an access allowed ACE with a different access

           // mask.

           //

           if (EqualSid(pUserSID,

              &(((ACCESS_ALLOWED_ACE *)pTempAce)->SidStart)))

              continue;

           //

           // STEP 13: Add the ACE to the new ACL.

           //

           if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,

                 ((PACE_HEADER) pTempAce)->AceSize)) {

              _tprintf(TEXT("AddAce() failed. Error %d\n"),

                    GetLastError());

              __leave;

           }

           newAceIndex++;

        }

     }

     //

     // STEP 14: Add the access-allowed ACE to the new DACL.

     // The new ACE added here will be in the correct position,

     // immediately after all existing non-inherited ACEs.

     //

     if (!AddAccessAllowedAce(pNewACL, ACL_REVISION2, dwAccessMask,

           pUserSID)) {

        _tprintf(TEXT("AddAccessAllowedAce() failed. Error %d\n"),

              GetLastError());

        __leave;

     }

     //

     // STEP 15: To conform to the new Windows 2000 preferred order,

     // we will now copy the rest of inherited ACEs from the

     // old DACL to the new DACL.

     //

     if (fDaclPresent && AclInfo.AceCount) {

        for (;

             CurrentAceIndex < AclInfo.AceCount;

             CurrentAceIndex++) {

           //

           // STEP 16: Get an ACE.

           //

           if (!GetAce(pACL, CurrentAceIndex, &pTempAce)) {

              _tprintf(TEXT("GetAce() failed. Error %d\n"),

                    GetLastError());

              __leave;

           }

           //

           // STEP 17: Add the ACE to the new ACL.

           //

           if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,

                 ((PACE_HEADER) pTempAce)->AceSize)) {

              _tprintf(TEXT("AddAce() failed. Error %d\n"),

                    GetLastError());

              __leave;

           }

        }

     }

     //

     // STEP 18: Set the new DACL to the new SD.

     //

     if (!SetSecurityDescriptorDacl(&newSD, TRUE, pNewACL,

           FALSE)) {

        _tprintf(TEXT("SetSecurityDescriptorDacl() failed. Error %d\n"),

              GetLastError());

        __leave;

     }

     //

     // STEP 19: Copy the old security descriptor control flags

     // regarding DACL automatic inheritance for Windows 2000 or

     // later where SetSecurityDescriptorControl() API is available

     // in advapi32.dll.

     //

     _SetSecurityDescriptorControl = (SetSecurityDescriptorControlFnPtr)

           GetProcAddress(GetModuleHandle(TEXT("advapi32.dll")),

           "SetSecurityDescriptorControl");

     if (_SetSecurityDescriptorControl) {

        SECURITY_DESCRIPTOR_CONTROL controlBitsOfInterest = 0;

        SECURITY_DESCRIPTOR_CONTROL controlBitsToSet = 0;

        SECURITY_DESCRIPTOR_CONTROL oldControlBits = 0;

        DWORD dwRevision = 0;

        if (!GetSecurityDescriptorControl(pFileSD, &oldControlBits,

           &dwRevision)) {

           _tprintf(TEXT("GetSecurityDescriptorControl() failed.")

                 TEXT("Error %d\n"), GetLastError());

           __leave;

        }

        if (oldControlBits & SE_DACL_AUTO_INHERITED) {

           controlBitsOfInterest =

              SE_DACL_AUTO_INHERIT_REQ |

              SE_DACL_AUTO_INHERITED;

           controlBitsToSet = controlBitsOfInterest;

        }

        else if (oldControlBits & SE_DACL_PROTECTED) {

           controlBitsOfInterest = SE_DACL_PROTECTED;

           controlBitsToSet = controlBitsOfInterest;

        }

       

        if (controlBitsOfInterest) {

           if (!_SetSecurityDescriptorControl(&newSD,

              controlBitsOfInterest,

              controlBitsToSet)) {

              _tprintf(TEXT("SetSecurityDescriptorControl() failed.")

                    TEXT("Error %d\n"), GetLastError());

              __leave;

           }

        }

     }

     //

     // STEP 20: Set the new SD to the File.

     //

     if (!SetFileSecurity(lpszFileName, secInfo,

           &newSD)) {

        _tprintf(TEXT("SetFileSecurity() failed. Error %d\n"),

              GetLastError());

        __leave;

     }

     fResult = TRUE;

  } __finally {

     //

     // STEP 21: Free allocated memory

     //

     if (pUserSID)

        myheapfree(pUserSID);

     if (szDomain)

        myheapfree(szDomain);

     if (pFileSD)

        myheapfree(pFileSD);

     if (pNewACL)

        myheapfree(pNewACL);

  }

 

  return fResult;

}

int _tmain(int argc, TCHAR *argv[]) {

  if (argc < 3) {

     _tprintf(TEXT("usage: \"%s\" <FileName> <AccountName>\n"), argv[0]);

     return 1;

  }

  // argv[1] - FileName

  // argv[2] - Name of the User or Group account to add access

  if (!AddAccessRights(argv[1], argv[2], GENERIC_ALL)) {

     _tprintf(TEXT("AddAccessRights() failed.\n"));

     return 1;

  }

  else {

     _tprintf(TEXT("AddAccessRights() succeeded.\n"));

     return 0;

  }

}

                               

Explanation of Steps in Sample Code

The LookupAccountName function is called to obtain the security identifier (SID) for the user or group name that is specified. The LookupAccountName function is actually called twice: once to determine the required buffer sizes, and then again to retrieve the account information. This SID obtained from this function is used later in a call to the AddAccessAllowedACE function. The LookupAccountName function also provides the domain where the user or group account is found.

NOTE: The LookupAccountName function can be a fairly expensive call, if multiple SAM databases need to be queried to retrieve the desired information.For additional information about getting the SID of the current user, click the article number below to view the article in the Microsoft Knowledge Base:

111544 HOWTO: Retrieve Current User and Domain Names on Windows NT, Windows 2000, or Windows XP

For additional information about getting the SID of well known or built in users or groups, click the article number below to view the article in the Microsoft Knowledge Base:

157234 HOWTO: Deal with Localized and Renamed User and Group Names

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 SD in 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.

Initialize a new SD by calling the InitializeSecurityDescriptor function.

The GetSecurityDescriptorDacl function retrieves a pointer to the DACL in the SD.

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.

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.

Allocate memory for the new ACL that ultimately contains the file's existing ACEs plus the access-allowed ACE.

Initialize the ACL structure.

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. If a DACL is present, copy all the ACEs from the old DACL to the new DACL. The sample code assumes that the old DACL is already in Windows 2000 preferred order. To conform to the new Windows 2000 preferred order for the new DACL, first the code copies all non-inherited ACEs from the old DACL to the new DACL, irrespective of the ACE type. The AceCount member of the ACL_SIZE_INFORMATION structure contains the number of ACEs in the DACL. Loop once for each ACE.

Retrieve a pointer to the ACE in the file's existing DACL.

Check if the ACE obtained is a non-inherited ACE. Copy only non-inherited ACEs in this first for function loop. If the ACE is an inherited ACE, break from this for function loop, so that the new access allowed non-inherited ACE can be added in the correct position, immediately after all existing non-inherited ACEs.

If the SID matches with the account specified, skip adding the ACE because we are adding an access allowed ACE with a different access mask.

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.

After copying all of the file's original non-inherited ACEs to the new ACL, add an access-allowed ACE, based on the value of dwAccessMask. The new ACE added here will be in the correct preferred order, immediately after all existing non-inherited ACEs.

If an old DACL was present, copy the rest of the inherited ACEs from the old DACL to the new DACL, starting from the CurrentAceIndex. Loop once for each inherited ACE.

Retrieve a pointer to the ACE in the file's existing DACL.

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.

Set the new DACL to the new SD by using the SetSecurityDescriptorDacl function.

Copy the old security descriptor control flags regarding DACL automatic inheritance to the new security descriptor. This is done by using SetSecurityDescriptorControl function which is available only on Windows 2000 and above. The sample code calls the API at runtime, only if the API is available in advapi32.dll.

Set the new 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.

Free the memory that was allocated in the sample code.

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.