gideon
asked on
Delphi Programming question
Has anybody got some sample code to use the security API with Windows NT, such as GetFileSecurity etc. ?
ASKER
Yes, I am still waiting. Nobody seems to know much about using the security stuff. Can you help ?
Hi this is article from SDK, example is good documented but in C source. If you need i can translate this to Delphi. (for translate please increase point if you have any)
PSS ID Number: Q102102
Authored 28-Jul-1993 Last modified 05-Jan-1995
The information in this article applies to:
- Microsoft Win32 Software Development Kit (SDK) for Windows NT
SUMMARY
This article explains the process of adding an access-allowed (or
access-denied) access control entry (ACE) to a file.
Adding an access-allowed ACE to a file's access control list (ACL) provides
a means of granting or denying (using an access-denied ACE) access to the
file to a particular user or group. In most cases, the file's ACL will not
have enough free space to add an additional ACE, and therefore it is
usually necessary to create a new ACL and copy the file's existing ACEs
over to it. Once the ACEs are copied over and the access-allowed ACE is
also added, the new ACL can be applied to the file's security descriptor
(SD). This process is explained in detail in the section below. Sample code
is provided at the end of this article.
MORE INFORMATION
At the end of this article is sample code that defines a function named
AddAccessRights(), which adds an access-allowed ACE to the specified file
allowing the specified access. Steps 1-17 in the comments of the sample
code are discussed in detail below:
1. GetUserName() is called to retrieve the name of the currently
logged in user. The user name is stored in plszUserName[] array.
2. LookupAccountName() is called to obtain the SID of the user
returned by GetUserName() in step 1. The resulting SID is stored
in the UserSID variable and will be used later in the
AddAccessAllowedACE() application programming interface (API)
call. The LookupAccountName() API is also providing the user's
domain in the plszDomain[] array. Please note that
LookupAccountName() returns the SID of the first user or group
that matches the name in plszUserName.
3. GetFileSecurity() is used here to obtain a copy of the file's
security descriptor (SD). The file's SD is placed into the ucSDbuf
variable, which is declared a size of
65536+SECURITY_DESCRIPTOR_ MIN_LENGTH for simplicity. This value
represents the maximum size of an SD, which ensures the SD will be
of sufficient size.
4. Here we initialize the new security descriptor (NewSD variable) by
calling the InitializeSecurityDescript or() API. Because the
SetFileSecurity() API requires that the security item being set is
contained in a SD, we create and initialize NewSD.
5. Here GetSecurityDescriptorDacl( ) retrieves a pointer to the
discretionary access control list (DACL) in the SD. The pointer is
stored in the pACL variable.
6. GetAclInformation() is called here to obtain size information on
the file's DACL in the form of a ACL_SIZE_INFORMATION structure.
This information is used when computing the size of the new DACL
and when copying ACEs.
7. This statement computes the exact number of bytes to allocate for
the new DACL. The AclBytesInUse member represents the number of
bytes being used in the file's DACL. We add this number to the
size of an ACCESS_ALLOWED_ACE and the size of the user's SID.
Subtracting the size of a DWORD is an adjustment required to
obtain the exact number of bytes necessary.
8. Here we allocate memory for the new ACL that will ultimately
contain the file's existing ACEs plus the access-allowed ACE.
9. In addition to allocating the memory, it is important to
initialize the ACL structure as we do here.
10. Here we check the bDaclPresent flag returned by
GetSecurityDescriptorDacl( ) to see if a DACL was present in the
file's SD. If a DACL was not present, then we skip the code that
copies the file's ACEs to the new DACL.
11. After verifying that there is at least one ACE in the file's DACL
(by checking the AceCount member), we begin the loop to copy the
individual ACEs to the new DACL.
12. Here we get a pointer to an ACE in the file's DACL by using the
GetAce() API.
13. Now we add the ACE to the new DACL. It is important to note that
we pass MAXDWORD for the dwStartingAceIndex parameter of AddAce()
to ensure the ACE is added to the end of the DACL. The statement
((PACE_HEADER)pTempAce)->A ceSize provides the size of the ACE.
14. Now that we have copied all the file's original ACEs over to our
new DACL, we add the access-allowed ACE. The dwAccessMask variable
will contain the access mask being granted. GENERIC_READ is an
example of an access mask.
15. Because the SetFileSecurity() API can set a variety of security
information, it takes a pointer to a security descriptor. For this
reason, it is necessary to attach our new DACL to a temporary SD.
This is done by using the SetSecurityDescriptorDacl( ) API.
16. Now that we have a SD containing the new DACL for the file, we set
the DACL to the file's SD by calling SetFileSecurity(). The
DACL_SECURITY_INFORMATION parameter indicates that we want the
DACL in the provided SD applied to the file's SD. Please note that
only the file's DACL is set, the other security information in the
file's SD remains unchanged.
17. Here we free the memory that was allocated for the new DACL.
The below sample demonstrates the basic steps required to add an access-
allowed ACE to a file's DACL. Please note that this same process can be
used to add an access-denied ACE to a file's DACL. Because the access-
denied ACE should appear before access-allowed ACEs, it is suggested that
the call to AddAccessDeniedAce() precede the code that copies the existing
ACEs to the new DACL.
Sample Code
-----------
#define SD_SIZE (65536 + SECURITY_DESCRIPTOR_MIN_LE NGTH)
BOOL AddAccessRights(CHAR *pFileName, DWORD dwAcessMask)
{
// SID variables
UCHAR psnuType[2048];
UCHAR lpszDomain[2048];
DWORD dwDomainLength = 250;
UCHAR UserSID[1024];
DWORD dwSIDBufSize=1024;
// User name variables
UCHAR lpszUserName[250];
DWORD dwUserNameLength = 250;
// File SD variables
UCHAR ucSDbuf[SD_SIZE];
PSECURITY_DESCRIPTOR pFileSD=(PSECURITY_DESCRIP TOR)ucSDbu f;
DWORD dwSDLengthNeeded;
// ACL variables
PACL pACL;
BOOL bDaclPresent;
BOOL bDaclDefaulted;
ACL_SIZE_INFORMATION AclInfo;
// New ACL variables
PACL pNewACL;
DWORD dwNewACLSize;
// New SD variables
UCHAR NewSD[SECURITY_DESCRIPTOR_ MIN_LENGTH ];
PSECURITY_DESCRIPTOR psdNewSD=(PSECURITY_DESCRI PTOR)NewSD ;
// Temporary ACE
PVOID pTempAce;
UINT CurrentAceIndex;
// STEP 1: Get the logged on user name
if(!GetUserName(lpszUserNa me,&dwUser NameLength ))
{
printf("Error %d:GetUserName\n",GetLastE rror());
return(FALSE);
}
// STEP 2: Get SID for current user
if (!LookupAccountName((LPSTR ) NULL,
lpszUserName,
UserSID,
&dwSIDBufSize,
lpszDomain,
&dwDomainLength,
(PSID_NAME_USE)psnuType))
{
printf("Error %d:LookupAccountName\n",Ge tLastError ());
return(FALSE);
}
// STEP 3: Get security descriptor (SD) for file
if(!GetFileSecurity(pFileN ame,
(SECURITY_INFORMATION)(DAC L_SECURITY _INFORMATI ON),
pFileSD,
SD_SIZE,
(LPDWORD)&dwSDLengthNeeded ))
{
printf("Error %d:GetFileSecurity\n",GetL astError() );
return(FALSE);
}
// STEP 4: Initialize new SD
if(!InitializeSecurityDesc riptor(psd NewSD,SECU RITY_DESCR IPTOR_REVI SION))
{
printf("Error %d:InitializeSecurityDescr iptor\n",G etLastErro r());
return(FALSE);
}
// STEP 5: Get DACL from SD
if (!GetSecurityDescriptorDac l(pFileSD,
&bDaclPresent,
&pACL,
&bDaclDefaulted))
{
printf("Error %d:GetSecurityDescriptorDa cl\n",GetL astError() );
return(FALSE);
}
// STEP 6: Get file ACL size information
if(!GetAclInformation(pACL ,&AclInfo, sizeof(ACL _SIZE_INFO RMATION),
AclSizeInformation))
{
printf("Error %d:GetAclInformation\n",Ge tLastError ());
return(FALSE);
}
// STEP 7: Compute size needed for the new ACL
dwNewACLSize = AclInfo.AclBytesInUse +
sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(UserSID) - sizeof(DWORD);
// STEP 8: Allocate memory for new ACL
pNewACL = (PACL)LocalAlloc(LPTR, dwNewACLSize);
// STEP 9: Initialize the new ACL
if(!InitializeAcl(pNewACL, dwNewACLSize, ACL_REVISION2))
{
printf("Error %d:InitializeAcl\n",GetLas tError());
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
// STEP 10: If DACL is present, copy it to a new DACL
if(bDaclPresent) // only copy if DACL was present
{
// STEP 11: Copy the file's ACEs to our new ACL
if(AclInfo.AceCount)
{
for(CurrentAceIndex = 0; CurrentAceIndex < AclInfo.AceCount;
CurrentAceIndex++)
{
// STEP 12: Get an ACE
if(!GetAce(pACL,CurrentAce Index,&pTe mpAce))
{
printf("Error %d: GetAce\n",GetLastError());
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
// STEP 13: Add the ACE to the new ACL
if(!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
((PACE_HEADER)pTempAce)->A ceSize))
{
printf("Error %d:AddAce\n",GetLastError( ));
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
}
}
}
// STEP 14: Add the access-allowed ACE to the new DACL
if(!AddAccessAllowedAce(pN ewACL,ACL_ REVISION2, dwAcessMas k, &UserSID))
{
printf("Error %d:AddAccessAllowedAce",Ge tLastError ());
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
// STEP 15: Set our new DACL to the file SD
if (!SetSecurityDescriptorDac l(psdNewSD ,
TRUE,
pNewACL,
FALSE))
{
printf("Error %d:SetSecurityDescriptorDa cl",GetLas tError());
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
// STEP 16: Set the SD to the File
if (!SetFileSecurity(pFileNam e, DACL_SECURITY_INFORMATION, psdNewSD))
{
printf("Error %d:SetFileSecurity\n",GetL astError() );
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
// STEP 17: Free the memory allocated for the new ACL
LocalFree((HLOCAL) pNewACL);
return(TRUE);
}
NOTE: Security descriptors have two possible formats: self-relative and
absolute. GetFileSecurity() returns an SD in self-relative format, but
SetFileSecurity() expects and absolute SD. This is one reason that the code
must create a new SD and copy the information, instead of simply modifying
the SD from GetFileSecurity() and passing it to SetFileSecurity(). It is
possible to call MakeAbsoluteSD() to do the conversion, but there may not
be enough room in the current ACL anyway, as mentioned above.
Additional reference words: 3.10 3.50
KBCategory: kbprg
KBSubcategory: BseSecurity
PSS ID Number: Q102102
Authored 28-Jul-1993 Last modified 05-Jan-1995
The information in this article applies to:
- Microsoft Win32 Software Development Kit (SDK) for Windows NT
SUMMARY
This article explains the process of adding an access-allowed (or
access-denied) access control entry (ACE) to a file.
Adding an access-allowed ACE to a file's access control list (ACL) provides
a means of granting or denying (using an access-denied ACE) access to the
file to a particular user or group. In most cases, the file's ACL will not
have enough free space to add an additional ACE, and therefore it is
usually necessary to create a new ACL and copy the file's existing ACEs
over to it. Once the ACEs are copied over and the access-allowed ACE is
also added, the new ACL can be applied to the file's security descriptor
(SD). This process is explained in detail in the section below. Sample code
is provided at the end of this article.
MORE INFORMATION
At the end of this article is sample code that defines a function named
AddAccessRights(), which adds an access-allowed ACE to the specified file
allowing the specified access. Steps 1-17 in the comments of the sample
code are discussed in detail below:
1. GetUserName() is called to retrieve the name of the currently
logged in user. The user name is stored in plszUserName[] array.
2. LookupAccountName() is called to obtain the SID of the user
returned by GetUserName() in step 1. The resulting SID is stored
in the UserSID variable and will be used later in the
AddAccessAllowedACE() application programming interface (API)
call. The LookupAccountName() API is also providing the user's
domain in the plszDomain[] array. Please note that
LookupAccountName() returns the SID of the first user or group
that matches the name in plszUserName.
3. GetFileSecurity() is used here to obtain a copy of the file's
security descriptor (SD). The file's SD is placed into the ucSDbuf
variable, which is declared a size of
65536+SECURITY_DESCRIPTOR_
represents the maximum size of an SD, which ensures the SD will be
of sufficient size.
4. Here we initialize the new security descriptor (NewSD variable) by
calling the InitializeSecurityDescript
SetFileSecurity() API requires that the security item being set is
contained in a SD, we create and initialize NewSD.
5. Here GetSecurityDescriptorDacl(
discretionary access control list (DACL) in the SD. The pointer is
stored in the pACL variable.
6. GetAclInformation() is called here to obtain size information on
the file's DACL in the form of a ACL_SIZE_INFORMATION structure.
This information is used when computing the size of the new DACL
and when copying ACEs.
7. This statement computes the exact number of bytes to allocate for
the new DACL. The AclBytesInUse member represents the number of
bytes being used in the file's DACL. We add this number to the
size of an ACCESS_ALLOWED_ACE and the size of the user's SID.
Subtracting the size of a DWORD is an adjustment required to
obtain the exact number of bytes necessary.
8. Here we allocate memory for the new ACL that will ultimately
contain the file's existing ACEs plus the access-allowed ACE.
9. In addition to allocating the memory, it is important to
initialize the ACL structure as we do here.
10. Here we check the bDaclPresent flag returned by
GetSecurityDescriptorDacl(
file's SD. If a DACL was not present, then we skip the code that
copies the file's ACEs to the new DACL.
11. After verifying that there is at least one ACE in the file's DACL
(by checking the AceCount member), we begin the loop to copy the
individual ACEs to the new DACL.
12. Here we get a pointer to an ACE in the file's DACL by using the
GetAce() API.
13. Now we add the ACE to the new DACL. It is important to note that
we pass MAXDWORD for the dwStartingAceIndex parameter of AddAce()
to ensure the ACE is added to the end of the DACL. The statement
((PACE_HEADER)pTempAce)->A
14. Now that we have copied all the file's original ACEs over to our
new DACL, we add the access-allowed ACE. The dwAccessMask variable
will contain the access mask being granted. GENERIC_READ is an
example of an access mask.
15. Because the SetFileSecurity() API can set a variety of security
information, it takes a pointer to a security descriptor. For this
reason, it is necessary to attach our new DACL to a temporary SD.
This is done by using the SetSecurityDescriptorDacl(
16. Now that we have a SD containing the new DACL for the file, we set
the DACL to the file's SD by calling SetFileSecurity(). The
DACL_SECURITY_INFORMATION parameter indicates that we want the
DACL in the provided SD applied to the file's SD. Please note that
only the file's DACL is set, the other security information in the
file's SD remains unchanged.
17. Here we free the memory that was allocated for the new DACL.
The below sample demonstrates the basic steps required to add an access-
allowed ACE to a file's DACL. Please note that this same process can be
used to add an access-denied ACE to a file's DACL. Because the access-
denied ACE should appear before access-allowed ACEs, it is suggested that
the call to AddAccessDeniedAce() precede the code that copies the existing
ACEs to the new DACL.
Sample Code
-----------
#define SD_SIZE (65536 + SECURITY_DESCRIPTOR_MIN_LE
BOOL AddAccessRights(CHAR *pFileName, DWORD dwAcessMask)
{
// SID variables
UCHAR psnuType[2048];
UCHAR lpszDomain[2048];
DWORD dwDomainLength = 250;
UCHAR UserSID[1024];
DWORD dwSIDBufSize=1024;
// User name variables
UCHAR lpszUserName[250];
DWORD dwUserNameLength = 250;
// File SD variables
UCHAR ucSDbuf[SD_SIZE];
PSECURITY_DESCRIPTOR pFileSD=(PSECURITY_DESCRIP
DWORD dwSDLengthNeeded;
// ACL variables
PACL pACL;
BOOL bDaclPresent;
BOOL bDaclDefaulted;
ACL_SIZE_INFORMATION AclInfo;
// New ACL variables
PACL pNewACL;
DWORD dwNewACLSize;
// New SD variables
UCHAR NewSD[SECURITY_DESCRIPTOR_
PSECURITY_DESCRIPTOR psdNewSD=(PSECURITY_DESCRI
// Temporary ACE
PVOID pTempAce;
UINT CurrentAceIndex;
// STEP 1: Get the logged on user name
if(!GetUserName(lpszUserNa
{
printf("Error %d:GetUserName\n",GetLastE
return(FALSE);
}
// STEP 2: Get SID for current user
if (!LookupAccountName((LPSTR
lpszUserName,
UserSID,
&dwSIDBufSize,
lpszDomain,
&dwDomainLength,
(PSID_NAME_USE)psnuType))
{
printf("Error %d:LookupAccountName\n",Ge
return(FALSE);
}
// STEP 3: Get security descriptor (SD) for file
if(!GetFileSecurity(pFileN
(SECURITY_INFORMATION)(DAC
pFileSD,
SD_SIZE,
(LPDWORD)&dwSDLengthNeeded
{
printf("Error %d:GetFileSecurity\n",GetL
return(FALSE);
}
// STEP 4: Initialize new SD
if(!InitializeSecurityDesc
{
printf("Error %d:InitializeSecurityDescr
return(FALSE);
}
// STEP 5: Get DACL from SD
if (!GetSecurityDescriptorDac
&bDaclPresent,
&pACL,
&bDaclDefaulted))
{
printf("Error %d:GetSecurityDescriptorDa
return(FALSE);
}
// STEP 6: Get file ACL size information
if(!GetAclInformation(pACL
AclSizeInformation))
{
printf("Error %d:GetAclInformation\n",Ge
return(FALSE);
}
// STEP 7: Compute size needed for the new ACL
dwNewACLSize = AclInfo.AclBytesInUse +
sizeof(ACCESS_ALLOWED_ACE)
GetLengthSid(UserSID) - sizeof(DWORD);
// STEP 8: Allocate memory for new ACL
pNewACL = (PACL)LocalAlloc(LPTR, dwNewACLSize);
// STEP 9: Initialize the new ACL
if(!InitializeAcl(pNewACL,
{
printf("Error %d:InitializeAcl\n",GetLas
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
// STEP 10: If DACL is present, copy it to a new DACL
if(bDaclPresent) // only copy if DACL was present
{
// STEP 11: Copy the file's ACEs to our new ACL
if(AclInfo.AceCount)
{
for(CurrentAceIndex = 0; CurrentAceIndex < AclInfo.AceCount;
CurrentAceIndex++)
{
// STEP 12: Get an ACE
if(!GetAce(pACL,CurrentAce
{
printf("Error %d: GetAce\n",GetLastError());
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
// STEP 13: Add the ACE to the new ACL
if(!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
((PACE_HEADER)pTempAce)->A
{
printf("Error %d:AddAce\n",GetLastError(
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
}
}
}
// STEP 14: Add the access-allowed ACE to the new DACL
if(!AddAccessAllowedAce(pN
{
printf("Error %d:AddAccessAllowedAce",Ge
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
// STEP 15: Set our new DACL to the file SD
if (!SetSecurityDescriptorDac
TRUE,
pNewACL,
FALSE))
{
printf("Error %d:SetSecurityDescriptorDa
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
// STEP 16: Set the SD to the File
if (!SetFileSecurity(pFileNam
{
printf("Error %d:SetFileSecurity\n",GetL
LocalFree((HLOCAL) pNewACL);
return(FALSE);
}
// STEP 17: Free the memory allocated for the new ACL
LocalFree((HLOCAL) pNewACL);
return(TRUE);
}
NOTE: Security descriptors have two possible formats: self-relative and
absolute. GetFileSecurity() returns an SD in self-relative format, but
SetFileSecurity() expects and absolute SD. This is one reason that the code
must create a new SD and copy the information, instead of simply modifying
the SD from GetFileSecurity() and passing it to SetFileSecurity(). It is
possible to call MakeAbsoluteSD() to do the conversion, but there may not
be enough room in the current ACL anyway, as mentioned above.
Additional reference words: 3.10 3.50
KBCategory: kbprg
KBSubcategory: BseSecurity
ASKER
Thanks, this works. BTW, I have been working on NT security stuff for the past week or so, and have created 18 VCL components that deal with security, networking, event logging etc. As soon as they are "ready" I will post it to DSP. This might be usefull for other people as well. Thanks !
So i can submit answer or not ?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Do you still waiting for help ?