473,402 Members | 2,053 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,402 software developers and data experts.

GetAce API in VB.Net

Hi,

I have been attempting to get at file security information using
vb.net. I have had some success so far. I am able to read the Security
Descriptor information of a file object, (Trustee, Access Mask etc) so
I think I am on the right track. I am currently stuck on the GetAce
Function. When I call it, LastDllError returns 127 the first time and
87 each time after that. (Until I restart the program) I stripped down
the program to the bare minimum to demonstrate the Problem. (Below) I
have successfully used the 'GetFileSecurity' and
'GetSecurityDescriptorDacl' functions in code to display the Security
Descriptor Information, so I believe that part of this code is
correct.

**************************************

Imports System.Runtime.InteropServices

Public Class SecurityAPI2
'This function retrieves the security information for a file.
Private Declare Function GetFileSecurity Lib "advapi32.dll" _
Alias "GetFileSecurityA" ( _
ByVal lpFileName As String, _
ByVal RequestedInformation As SECURITY_INFORMATION, _
ByVal pSecurityDescriptor As IntPtr, _
ByVal nLength As Int32, _
ByRef lpnLengthNeeded As Int32) As Boolean

' This function retrieves the DACL from the file's security
descriptor.
Private Declare Function GetSecurityDescriptorDacl Lib
"advapi32.dll" ( _
ByVal pSecurityDescriptor As IntPtr, _
ByRef lpbDaclPresent As Boolean, _
ByRef pDacl As IntPtr, _
ByRef lpbDaclDefaulted As Boolean) As Boolean

Declare Function GetAce Lib "advapi32.dll" ( _
ByRef pAcl As IntPtr, _
ByVal dwAceIndex As Integer, _
ByRef pAce As IntPtr) As Boolean

' This enumeration tells what type of information we want to
retrieve
' about the file's security.
Public Enum SECURITY_INFORMATION
OWNER_SECURITY_INFORMATION = &H1
GROUP_SECURITY_INFORMATION = &H2
DACL_SECURITY_INFORMATION = &H4
SACL_SECURITY_INFORMATION = &H8
PROTECTED_DACL_SECURITY_INFORMATION = &H80000000
PROTECTED_SACL_SECURITY_INFORMATION = &H40000000
UNPROTECTED_DACL_SECURITY_INFORMATION = &H20000000
UNPROTECTED_SACL_SECURITY_INFORMATION = &H10000000
End Enum

Const ERROR_INSUFFICIENT_BUFFER As Integer = 122

Public Function ReadFileSecurity(ByVal sPath As String)
Dim SD As IntPtr
' Get the old SD
SD = GetExistingSD(sPath,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION)

' Obtain the DACL.
Dim DACLPresent As Boolean ' Is the DACL present?
Dim Defaulted As Boolean ' Is the DACL
defaulted?
Dim DACL As IntPtr ' Pointer to the DACL.

If (GetSecurityDescriptorDacl(SD, DACLPresent, DACL,
Defaulted) <> True) Then
MsgBox("Unable to Get Dacl")
End If

' Obtain the array of ACEs from the DACL.
Dim bSuccess As Integer
Dim ACECount As Integer ' Number of ACEs in
DACL.
Dim ACEList As IntPtr ' An array of ACE
entries.

Dim pAce As IntPtr
bSuccess = GetAce(DACL, 0, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If
End Function

' This function encapsulates the code required to obtain a
security
' descriptor.
Public Function GetExistingSD( _
ByVal sPath As String, _
ByVal SEType As SECURITY_INFORMATION) As IntPtr

Dim bSuccess As Integer 'Status variable
Dim SD As IntPtr
Dim SDSize As Int32 ' Actual size of the
security descriptor.
Dim SDSizeNeeded As Int32 ' Required security
descriptor size.

' Call GetFileSecurity the first time to obtain the size of
the Buffer
' required for the Security Descriptor.
SDSizeNeeded = 0
bSuccess = GetFileSecurity(sPath, SEType, SD, 0, SDSizeNeeded)
' exit if any error
If (bSuccess = 0) And (Err.LastDllError <>
ERROR_INSUFFICIENT_BUFFER) Then
MsgBox("Failure")
End If

'Determine the size of the security descriptor
SD = Marshal.AllocHGlobal(SDSizeNeeded)
SDSize = SDSizeNeeded
bSuccess = GetFileSecurity(sPath, SEType, SD, SDSize,
SDSizeNeeded)
If bSuccess = 0 Then
MsgBox("Failure")
End If

Return SD
End Function
End Class
Nov 21 '05 #1
9 3707
> Declare Function GetAce Lib "advapi32.dll" ( _
ByRef pAcl As IntPtr, _
ByVal dwAceIndex As Integer, _
ByRef pAce As IntPtr) As Boolean


Change pAcl from 'ByRef' to 'ByVal'.

hope that helps..
Imran.
Nov 21 '05 #2
Imran, Thanks for spotting that (doh!) mistake. I knew that. Just
staring at the code too long. However, after fixing it, I am getting
the same errors on the next function in my code, AddAce(). I double
checked the documentation this time and I believe the function should
be defined with all 5 parameters using ByVal. (The first one is
ambiguous, but since ByRef throws an exception I think I'll go with
ByVal!)

Anyway, here are additions to my code (following the GetAce call) and
the relevant Functions, Structures and Enumerations:

Dave

*************************************************

Const ACL_REVISION = 2
ACECount = 1
Dim pNewACL As IntPtr
bSuccess = InitializeAcl(pNewACL, ACECount *
EXPLICIT_ACCESS.SIZEOF_EA1, ACL_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New ACL")
Exit Function
End If

bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
EXPLICIT_ACCESS.SIZEOF_EA1)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If
Declare Function InitializeAcl Lib "advapi32.dll" ( _
ByRef pAcl As IntPtr, _
ByVal nAclLength As Integer, _
ByVal dwAclRevision As Integer) As Integer

Declare Function AddAce Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal dwAceRevision As Integer, _
ByVal dwStartingAceIndex As Integer, _
ByVal pAceList As IntPtr, _
ByVal nAceListLength As Integer) As Integer

' This data Structure is used to create the explicit entry array.
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure EXPLICIT_ACCESS
Public Shared ReadOnly SIZEOF_EA1 As Integer
Shared Sub New()
SIZEOF_EA1 = Marshal.SizeOf(GetType(EXPLICIT_ACCESS))
End Sub
Public grfAccessPermissions As UInt32
Public grfAccessMode As ACCESS_MODE
Public grfInheritance As UInt32
Public Trustee As Trustee
End Structure

' This Structure contains the trustee information for the ACE.
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure TRUSTEE
Public pMultipleTrustee As IntPtr
Public MultipleTrusteeOperation As MULTIPLE_TRUSTEE_OPERATION
Public TrusteeForm As TRUSTEE_FORM
Public TrusteeType As TRUSTEE_TYPE
Public ptstrName As IntPtr
End Structure

' The ACCESS_MODE enumeration tells what type of ACE entry we're
' working with.
Public Enum ACCESS_MODE
NOT_USED_ACCESS = 0
GRANT_ACCESS
SET_ACCESS
DENY_ACCESS
REVOKE_ACCESS
SET_AUDIT_SUCCESS
SET_AUDIT_FAILURE
End Enum

' The MULTIPLE_TRUSTEE_OPERATION enumeration determines if this
' is a single or a multiple trustee.
Public Enum MULTIPLE_TRUSTEE_OPERATION
NO_MULTIPLE_TRUSTEE
TRUSTEE_IS_IMPERSONATE
End Enum

' The TRUSTEE_FORM enumeration determines what form the ACE
trustee
' takes.
Public Enum TRUSTEE_FORM
TRUSTEE_IS_SID
TRUSTEE_IS_NAME
TRUSTEE_BAD_FORM
TRUSTEE_IS_OBJECTS_AND_SID
TRUSTEE_IS_OBJECTS_AND_NAME
End Enum

' The TRUSTEE_TYPE enumeration determins the type of the trustee.
Public Enum TRUSTEE_TYPE
TRUSTEE_IS_UNKNOWN
TRUSTEE_IS_USER
TRUSTEE_IS_GROUP
TRUSTEE_IS_DOMAIN
TRUSTEE_IS_ALIAS
TRUSTEE_IS_WELL_KNOWN_GROUP
TRUSTEE_IS_DELETED
TRUSTEE_IS_INVALID
TRUSTEE_IS_COMPUTER
End Enum

"Imran Koradia" <no****@microsoft.com> wrote in message news:<ed**************@TK2MSFTNGP10.phx.gbl>...
Declare Function GetAce Lib "advapi32.dll" ( _
ByRef pAcl As IntPtr, _
ByVal dwAceIndex As Integer, _
ByRef pAce As IntPtr) As Boolean


Change pAcl from 'ByRef' to 'ByVal'.

hope that helps..
Imran.

Nov 21 '05 #3
Dave,

I'm not too familiar with the security APIs. However, I don't think the
EXPLICIT_ACCESS structure can be used with the AddAce function (Instead,
this structure is used in functions such as SetEntriesInAcl,
GetExplicitEntriesFromAcl, etc). The structure that goes with it are the
various ACE structures (ALLOWED_ACCESS_ACE, etc). However, as I said, I'm
not too familar with the security APIs and I could be wrong. I did try using
the ALLOWED_ACCESS_ACE with AddAce and it did seem to work (atleast it
didn't throw up an error). Here's the code that I have:

<StructLayoutAttribute(LayoutKind.Sequential)> _
Public Structure ACL_SIZE_INFORMATION
Public AceCount As Integer
Public AclBytesInUse As Integer
Public AclBytesFree As Integer
End Structure

Public Structure ACE_HEADER
Public AceType As Byte
Public AceFlags As Byte
Public AceSize As Short
End Structure

Public Structure ACCESS_ALLOWED_ACE
Public Header As ACE_HEADER
Public Mask As Integer
Public SidStart As Integer
End Structure

Public Structure ACL
Dim AclRevision As Byte
Dim Sbz1 As Byte
Dim AclSize As Short
Dim AceCount As Short
Dim Sbz2 As Short
End Structure

Declare Function InitializeAcl Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal nAclLength As Integer, _
ByVal dwAclRevision As Integer) As Integer

Declare Function AddAce Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal dwAceRevision As Integer, _
ByVal dwStartingAceIndex As Integer, _
ByVal pAceList As IntPtr, _
ByVal nAceListLength As Integer) As Integer

Declare Auto Function GetAclInformation Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal pAclInformation As IntPtr, _
ByVal AclInformationLength As Integer, _
ByVal dwAclInformationClass As Integer) As Integer
Public Function ReadFileSecurity(ByVal sPath As String) As IntPtr
' from winnt.h
Const SID_MAX_SUB_AUTHORITIES As Integer = 15
Dim SD As IntPtr
' Get the old SD
SD = GetExistingSD(sPath,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION)

' Obtain the DACL.
Dim DACLPresent As Boolean ' Is the DACL present?
Dim Defaulted As Boolean ' Is the DACL defaulted?
Dim DACL As IntPtr ' Pointer to the DACL.

If (GetSecurityDescriptorDacl(SD, DACLPresent, DACL, Defaulted) = 0)
Then
MsgBox("Unable to Get Dacl")
End If

' Obtain the array of ACEs from the DACL.
Dim bSuccess As Integer
Dim ACECount As Integer ' Number of ACEs in DACL.
Dim ACEList As IntPtr ' An array of ACE entries.

Dim sizePtr As IntPtr =
Marshal.AllocHGlobal(Marshal.SizeOf(GetType(ACL_SI ZE_INFORMATION)))
Dim AclInformationLength As Integer =
Marshal.SizeOf(GetType(ACL_SIZE_INFORMATION))
If GetAclInformation(DACL, sizePtr, AclInformationLength, 2) = 0
Then
MsgBox("error: " & Err.LastDllError)
End If

Dim size As Integer = CType(Marshal.PtrToStructure(sizePtr,
GetType(ACL_SIZE_INFORMATION)), ACL_SIZE_INFORMATION).AclBytesInUse

Const ACL_REVISION As Integer = 2
ACECount = 1
Dim pNewACL As IntPtr
Dim newsize As Integer = size +
Marshal.SizeOf(GetType(ACCESS_ALLOWED_ACE)) -
Marshal.SizeOf(GetType(Integer)) + (SID_MAX_SUB_AUTHORITIES *
Marshal.SizeOf(GetType(Integer)))
pNewACL = Marshal.AllocHGlobal(newsize)
bSuccess = 0
bSuccess = InitializeAcl(pNewACL, newsize, ACL_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New ACL")
Exit Function
End If

Dim pAce As IntPtr
bSuccess = GetAce(DACL, 0, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If

Dim AceStruct As ACCESS_ALLOWED_ACE =
CType(Marshal.PtrToStructure(pAce, GetType(ACCESS_ALLOWED_ACE)),
ACCESS_ALLOWED_ACE)

bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
AceStruct.Header.AceSize)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If

End Function
I'm not sure if that's what you are looking for. If you are looking to use
the EXPLICIT_ACCESS structure, you might want to look into the functions
that go with it (which I mentioned earlier - they are in the 'See Also'
section in the documentation for the structure).

hope that helps..
Imran.

"Dave Coate" <co*****@comcast.net> wrote in message
news:ed**************************@posting.google.c om...
Imran, Thanks for spotting that (doh!) mistake. I knew that. Just
staring at the code too long. However, after fixing it, I am getting
the same errors on the next function in my code, AddAce(). I double
checked the documentation this time and I believe the function should
be defined with all 5 parameters using ByVal. (The first one is
ambiguous, but since ByRef throws an exception I think I'll go with
ByVal!)

Anyway, here are additions to my code (following the GetAce call) and
the relevant Functions, Structures and Enumerations:

Dave

*************************************************

Const ACL_REVISION = 2
ACECount = 1
Dim pNewACL As IntPtr
bSuccess = InitializeAcl(pNewACL, ACECount *
EXPLICIT_ACCESS.SIZEOF_EA1, ACL_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New ACL")
Exit Function
End If

bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
EXPLICIT_ACCESS.SIZEOF_EA1)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If
Declare Function InitializeAcl Lib "advapi32.dll" ( _
ByRef pAcl As IntPtr, _
ByVal nAclLength As Integer, _
ByVal dwAclRevision As Integer) As Integer

Declare Function AddAce Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal dwAceRevision As Integer, _
ByVal dwStartingAceIndex As Integer, _
ByVal pAceList As IntPtr, _
ByVal nAceListLength As Integer) As Integer

' This data Structure is used to create the explicit entry array.
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure EXPLICIT_ACCESS
Public Shared ReadOnly SIZEOF_EA1 As Integer
Shared Sub New()
SIZEOF_EA1 = Marshal.SizeOf(GetType(EXPLICIT_ACCESS))
End Sub
Public grfAccessPermissions As UInt32
Public grfAccessMode As ACCESS_MODE
Public grfInheritance As UInt32
Public Trustee As Trustee
End Structure

' This Structure contains the trustee information for the ACE.
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure TRUSTEE
Public pMultipleTrustee As IntPtr
Public MultipleTrusteeOperation As MULTIPLE_TRUSTEE_OPERATION
Public TrusteeForm As TRUSTEE_FORM
Public TrusteeType As TRUSTEE_TYPE
Public ptstrName As IntPtr
End Structure

' The ACCESS_MODE enumeration tells what type of ACE entry we're
' working with.
Public Enum ACCESS_MODE
NOT_USED_ACCESS = 0
GRANT_ACCESS
SET_ACCESS
DENY_ACCESS
REVOKE_ACCESS
SET_AUDIT_SUCCESS
SET_AUDIT_FAILURE
End Enum

' The MULTIPLE_TRUSTEE_OPERATION enumeration determines if this
' is a single or a multiple trustee.
Public Enum MULTIPLE_TRUSTEE_OPERATION
NO_MULTIPLE_TRUSTEE
TRUSTEE_IS_IMPERSONATE
End Enum

' The TRUSTEE_FORM enumeration determines what form the ACE
trustee
' takes.
Public Enum TRUSTEE_FORM
TRUSTEE_IS_SID
TRUSTEE_IS_NAME
TRUSTEE_BAD_FORM
TRUSTEE_IS_OBJECTS_AND_SID
TRUSTEE_IS_OBJECTS_AND_NAME
End Enum

' The TRUSTEE_TYPE enumeration determins the type of the trustee.
Public Enum TRUSTEE_TYPE
TRUSTEE_IS_UNKNOWN
TRUSTEE_IS_USER
TRUSTEE_IS_GROUP
TRUSTEE_IS_DOMAIN
TRUSTEE_IS_ALIAS
TRUSTEE_IS_WELL_KNOWN_GROUP
TRUSTEE_IS_DELETED
TRUSTEE_IS_INVALID
TRUSTEE_IS_COMPUTER
End Enum

"Imran Koradia" <no****@microsoft.com> wrote in message

news:<ed**************@TK2MSFTNGP10.phx.gbl>...
Declare Function GetAce Lib "advapi32.dll" ( _
ByRef pAcl As IntPtr, _
ByVal dwAceIndex As Integer, _
ByRef pAce As IntPtr) As Boolean


Change pAcl from 'ByRef' to 'ByVal'.

hope that helps..
Imran.

Nov 21 '05 #4
Imran,

Thanks so much for your help so far.

This code seems to run without error for me as well. I am attempting to
understand how to work with file security descriptors. In the simplest case
I want to create a new file security descriptor exactly like the old one and
place it back on the file. This seems to be accomplished by looping through
the old SD DACL aces and placing them in a new SD DACL. From there, I should
be able to selectively exclude some ACEs from the AddAce function (to
effectively remove it from the SD) or add a new ACE using the
AddAccessAllowedAce function after adding all the old ones.

I was able to get a count of the aces in the old DACL from your example with
the following:
nCLS = CType(Marshal.PtrToStructure(sizePtr, GetType(ACL_SIZE_INFORMATION)),
ACL_SIZE_INFORMATION).AceCount

Therefore I can use the following Loop:
Dim ix As Integer
For ix = 0 To nACLS - 1

'************************ Get Each Ace
****************************
Dim pAce As IntPtr
bSuccess = GetAce(DACL, ix, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If
'********************** End Get Each Ace
**************************

'************************ Add Each Ace
****************************
Dim AceStruct As ACCESS_ALLOWED_ACE = _
CType(Marshal.PtrToStructure(pAce,
GetType(ACCESS_ALLOWED_ACE)), _
ACCESS_ALLOWED_ACE)

bSuccess = AddAce(pNewACL, ACL_REVISION, ix, pAce, _
AceStruct.Header.AceSize)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If
'********************** End Get Each Ace
**************************
Next

I can confirm that this works by building a function that gets the Ace Count
of an ACL. By checking this count on both the (original) DACL and the (new)
pNewACL I can see this number go up and down as I add and subtract users
with access to the file through the expolorer interface. So, I think All I
have left to do is to initialize a new Security Descriptor, set the new DACL
to this Security Descriptor and set this SD to the file. I have the
following code:

Private Declare Function InitializeSecurityDescriptor Lib "advapi32.dll"
( _
ByRef pSecurityDescriptor As IntPtr, _
ByVal dwRevision As Integer) As Boolean

Declare Function SetSecurityDescriptorDacl Lib "advapi32.dll" ( _
ByRef pSecurityDescriptor As IntPtr, _
ByVal bDaclPresent As Integer, _
ByVal pDacl As IntPtr, _
ByVal bDaclDefaulted As Integer) As Integer

Declare Function SetFileSecurity Lib "advapi32.dll" Alias
"SetFileSecurityA" ( _
ByVal lpFileName As String, _
ByVal SecurityInformation As Integer, _
ByVal pSecurityDescriptor As IntPtr) As Integer

'************* Initialize a new Security Descriptor
*******************
' Call InitializeSecurityDescriptor to build a new SD for the file.
Dim NewSD As IntPtr

'Set the size of the new sd to SecDescSize"
'SDSizeNeeded comes from the initial call to GetFileSecurity
'Therefore I am setting the new SD to the size of the old SD for
this experiment
NewSD = Marshal.AllocHGlobal(SDSizeNeeded)

bSuccess = InitializeSecurityDescriptor(NewSD, _
SECURITY_DESCRIPTOR_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New Security Descriptor " _
& Err.LastDllError)
Exit Function
End If
'************* End Initialize a new Security Descriptor
***************

'**************** Set the Security Descriptor Dacl
********************
' Set the file's Security Descriptor to the new DACL.
bSuccess = SetSecurityDescriptorDacl(NewSD, 1, pNewACL, 0)
' Make sure you set the SD to the new DACL.
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set New DACL to Security Descriptor")
Exit Function
End If
'************** End Set the Security Descriptor Dacl
******************

'Debug - This is my test function and I get the correct number of
ACEs
GetAceCount(pNewACL)

'********************* Set the File Security
**************************
' The final step is to add the Security Descriptor back to the file
bSuccess = SetFileSecurity(sPath, _
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, NewSD)

' Make sure you added the Security Descriptor to the file!
If (bSuccess = 0) Then
MsgBox("Error: Unable to Set New Security Descriptor " _
& " to File : " & sPath)
MsgBox(Err.LastDllError)
Else
MsgBox("Updated Security Descriptor on File: " _
& sPath)
End If
'******************* End Set the File Security
************************

When I put this all together the SetFileSecurity function returns 998 for
LastDllError which seems to be an access denied (?) error. I am an
administrator on the computer where the file resides.

What am I doing wrong now?
Dave

"Imran Koradia" <no****@microsoft.com> wrote in message
news:ud*************@tk2msftngp13.phx.gbl...
Dave,

I'm not too familiar with the security APIs. However, I don't think the
EXPLICIT_ACCESS structure can be used with the AddAce function (Instead,
this structure is used in functions such as SetEntriesInAcl,
GetExplicitEntriesFromAcl, etc). The structure that goes with it are the
various ACE structures (ALLOWED_ACCESS_ACE, etc). However, as I said, I'm
not too familar with the security APIs and I could be wrong. I did try using the ALLOWED_ACCESS_ACE with AddAce and it did seem to work (atleast it
didn't throw up an error). Here's the code that I have:

<StructLayoutAttribute(LayoutKind.Sequential)> _
Public Structure ACL_SIZE_INFORMATION
Public AceCount As Integer
Public AclBytesInUse As Integer
Public AclBytesFree As Integer
End Structure

Public Structure ACE_HEADER
Public AceType As Byte
Public AceFlags As Byte
Public AceSize As Short
End Structure

Public Structure ACCESS_ALLOWED_ACE
Public Header As ACE_HEADER
Public Mask As Integer
Public SidStart As Integer
End Structure

Public Structure ACL
Dim AclRevision As Byte
Dim Sbz1 As Byte
Dim AclSize As Short
Dim AceCount As Short
Dim Sbz2 As Short
End Structure

Declare Function InitializeAcl Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal nAclLength As Integer, _
ByVal dwAclRevision As Integer) As Integer

Declare Function AddAce Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal dwAceRevision As Integer, _
ByVal dwStartingAceIndex As Integer, _
ByVal pAceList As IntPtr, _
ByVal nAceListLength As Integer) As Integer

Declare Auto Function GetAclInformation Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal pAclInformation As IntPtr, _
ByVal AclInformationLength As Integer, _
ByVal dwAclInformationClass As Integer) As Integer
Public Function ReadFileSecurity(ByVal sPath As String) As IntPtr
' from winnt.h
Const SID_MAX_SUB_AUTHORITIES As Integer = 15
Dim SD As IntPtr
' Get the old SD
SD = GetExistingSD(sPath,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION)

' Obtain the DACL.
Dim DACLPresent As Boolean ' Is the DACL present?
Dim Defaulted As Boolean ' Is the DACL defaulted?
Dim DACL As IntPtr ' Pointer to the DACL.

If (GetSecurityDescriptorDacl(SD, DACLPresent, DACL, Defaulted) = 0) Then
MsgBox("Unable to Get Dacl")
End If

' Obtain the array of ACEs from the DACL.
Dim bSuccess As Integer
Dim ACECount As Integer ' Number of ACEs in DACL.
Dim ACEList As IntPtr ' An array of ACE entries.

Dim sizePtr As IntPtr =
Marshal.AllocHGlobal(Marshal.SizeOf(GetType(ACL_SI ZE_INFORMATION)))
Dim AclInformationLength As Integer =
Marshal.SizeOf(GetType(ACL_SIZE_INFORMATION))
If GetAclInformation(DACL, sizePtr, AclInformationLength, 2) = 0
Then
MsgBox("error: " & Err.LastDllError)
End If

Dim size As Integer = CType(Marshal.PtrToStructure(sizePtr,
GetType(ACL_SIZE_INFORMATION)), ACL_SIZE_INFORMATION).AclBytesInUse

Const ACL_REVISION As Integer = 2
ACECount = 1
Dim pNewACL As IntPtr
Dim newsize As Integer = size +
Marshal.SizeOf(GetType(ACCESS_ALLOWED_ACE)) -
Marshal.SizeOf(GetType(Integer)) + (SID_MAX_SUB_AUTHORITIES *
Marshal.SizeOf(GetType(Integer)))
pNewACL = Marshal.AllocHGlobal(newsize)
bSuccess = 0
bSuccess = InitializeAcl(pNewACL, newsize, ACL_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New ACL")
Exit Function
End If

Dim pAce As IntPtr
bSuccess = GetAce(DACL, 0, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If

Dim AceStruct As ACCESS_ALLOWED_ACE =
CType(Marshal.PtrToStructure(pAce, GetType(ACCESS_ALLOWED_ACE)),
ACCESS_ALLOWED_ACE)

bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
AceStruct.Header.AceSize)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If

End Function
I'm not sure if that's what you are looking for. If you are looking to use
the EXPLICIT_ACCESS structure, you might want to look into the functions
that go with it (which I mentioned earlier - they are in the 'See Also'
section in the documentation for the structure).

hope that helps..
Imran.

"Dave Coate" <co*****@comcast.net> wrote in message
news:ed**************************@posting.google.c om...
Imran, Thanks for spotting that (doh!) mistake. I knew that. Just
staring at the code too long. However, after fixing it, I am getting
the same errors on the next function in my code, AddAce(). I double
checked the documentation this time and I believe the function should
be defined with all 5 parameters using ByVal. (The first one is
ambiguous, but since ByRef throws an exception I think I'll go with
ByVal!)

Anyway, here are additions to my code (following the GetAce call) and
the relevant Functions, Structures and Enumerations:

Dave

*************************************************

Const ACL_REVISION = 2
ACECount = 1
Dim pNewACL As IntPtr
bSuccess = InitializeAcl(pNewACL, ACECount *
EXPLICIT_ACCESS.SIZEOF_EA1, ACL_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New ACL")
Exit Function
End If

bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
EXPLICIT_ACCESS.SIZEOF_EA1)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If
Declare Function InitializeAcl Lib "advapi32.dll" ( _
ByRef pAcl As IntPtr, _
ByVal nAclLength As Integer, _
ByVal dwAclRevision As Integer) As Integer

Declare Function AddAce Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal dwAceRevision As Integer, _
ByVal dwStartingAceIndex As Integer, _
ByVal pAceList As IntPtr, _
ByVal nAceListLength As Integer) As Integer

' This data Structure is used to create the explicit entry array.
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure EXPLICIT_ACCESS
Public Shared ReadOnly SIZEOF_EA1 As Integer
Shared Sub New()
SIZEOF_EA1 = Marshal.SizeOf(GetType(EXPLICIT_ACCESS))
End Sub
Public grfAccessPermissions As UInt32
Public grfAccessMode As ACCESS_MODE
Public grfInheritance As UInt32
Public Trustee As Trustee
End Structure

' This Structure contains the trustee information for the ACE.
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure TRUSTEE
Public pMultipleTrustee As IntPtr
Public MultipleTrusteeOperation As MULTIPLE_TRUSTEE_OPERATION
Public TrusteeForm As TRUSTEE_FORM
Public TrusteeType As TRUSTEE_TYPE
Public ptstrName As IntPtr
End Structure

' The ACCESS_MODE enumeration tells what type of ACE entry we're
' working with.
Public Enum ACCESS_MODE
NOT_USED_ACCESS = 0
GRANT_ACCESS
SET_ACCESS
DENY_ACCESS
REVOKE_ACCESS
SET_AUDIT_SUCCESS
SET_AUDIT_FAILURE
End Enum

' The MULTIPLE_TRUSTEE_OPERATION enumeration determines if this
' is a single or a multiple trustee.
Public Enum MULTIPLE_TRUSTEE_OPERATION
NO_MULTIPLE_TRUSTEE
TRUSTEE_IS_IMPERSONATE
End Enum

' The TRUSTEE_FORM enumeration determines what form the ACE
trustee
' takes.
Public Enum TRUSTEE_FORM
TRUSTEE_IS_SID
TRUSTEE_IS_NAME
TRUSTEE_BAD_FORM
TRUSTEE_IS_OBJECTS_AND_SID
TRUSTEE_IS_OBJECTS_AND_NAME
End Enum

' The TRUSTEE_TYPE enumeration determins the type of the trustee.
Public Enum TRUSTEE_TYPE
TRUSTEE_IS_UNKNOWN
TRUSTEE_IS_USER
TRUSTEE_IS_GROUP
TRUSTEE_IS_DOMAIN
TRUSTEE_IS_ALIAS
TRUSTEE_IS_WELL_KNOWN_GROUP
TRUSTEE_IS_DELETED
TRUSTEE_IS_INVALID
TRUSTEE_IS_COMPUTER
End Enum

"Imran Koradia" <no****@microsoft.com> wrote in message

news:<ed**************@TK2MSFTNGP10.phx.gbl>...
> Declare Function GetAce Lib "advapi32.dll" ( _
> ByRef pAcl As IntPtr, _
> ByVal dwAceIndex As Integer, _
> ByRef pAce As IntPtr) As Boolean

Change pAcl from 'ByRef' to 'ByVal'.

hope that helps..
Imran.


Nov 21 '05 #5
Imran,

If you are still watching this thread, please disregard my last post. I
took (yet another) look at the documentation and noticed that
'SetFileSecurity' is now 'obsolete'. I used SetNamedSecurityInfo instead and
a lot of things are falling into place. There seems to be multiple API
functions (2 sets?) for working with Security. I am confused about this. I
read somewhere there is a 'High-Level' set of functions and a 'Low-Level'
set of functions. Is this what I am seeing here? Is there a way to know
which is which?

Thanks again,
Dave
"Dave Coate" <co*****@comcast.net> wrote in message
news:uP****************@TK2MSFTNGP12.phx.gbl...
Imran,

Thanks so much for your help so far.

This code seems to run without error for me as well. I am attempting to
understand how to work with file security descriptors. In the simplest case I want to create a new file security descriptor exactly like the old one and place it back on the file. This seems to be accomplished by looping through the old SD DACL aces and placing them in a new SD DACL. From there, I should be able to selectively exclude some ACEs from the AddAce function (to
effectively remove it from the SD) or add a new ACE using the
AddAccessAllowedAce function after adding all the old ones.

I was able to get a count of the aces in the old DACL from your example with the following:
nCLS = CType(Marshal.PtrToStructure(sizePtr, GetType(ACL_SIZE_INFORMATION)), ACL_SIZE_INFORMATION).AceCount

Therefore I can use the following Loop:
Dim ix As Integer
For ix = 0 To nACLS - 1

'************************ Get Each Ace
****************************
Dim pAce As IntPtr
bSuccess = GetAce(DACL, ix, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If
'********************** End Get Each Ace
**************************

'************************ Add Each Ace
****************************
Dim AceStruct As ACCESS_ALLOWED_ACE = _
CType(Marshal.PtrToStructure(pAce,
GetType(ACCESS_ALLOWED_ACE)), _
ACCESS_ALLOWED_ACE)

bSuccess = AddAce(pNewACL, ACL_REVISION, ix, pAce, _
AceStruct.Header.AceSize)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If
'********************** End Get Each Ace
**************************
Next

I can confirm that this works by building a function that gets the Ace Count of an ACL. By checking this count on both the (original) DACL and the (new) pNewACL I can see this number go up and down as I add and subtract users
with access to the file through the expolorer interface. So, I think All I
have left to do is to initialize a new Security Descriptor, set the new DACL to this Security Descriptor and set this SD to the file. I have the
following code:

Private Declare Function InitializeSecurityDescriptor Lib "advapi32.dll" ( _
ByRef pSecurityDescriptor As IntPtr, _
ByVal dwRevision As Integer) As Boolean

Declare Function SetSecurityDescriptorDacl Lib "advapi32.dll" ( _
ByRef pSecurityDescriptor As IntPtr, _
ByVal bDaclPresent As Integer, _
ByVal pDacl As IntPtr, _
ByVal bDaclDefaulted As Integer) As Integer

Declare Function SetFileSecurity Lib "advapi32.dll" Alias
"SetFileSecurityA" ( _
ByVal lpFileName As String, _
ByVal SecurityInformation As Integer, _
ByVal pSecurityDescriptor As IntPtr) As Integer

'************* Initialize a new Security Descriptor
*******************
' Call InitializeSecurityDescriptor to build a new SD for the file. Dim NewSD As IntPtr

'Set the size of the new sd to SecDescSize"
'SDSizeNeeded comes from the initial call to GetFileSecurity
'Therefore I am setting the new SD to the size of the old SD for
this experiment
NewSD = Marshal.AllocHGlobal(SDSizeNeeded)

bSuccess = InitializeSecurityDescriptor(NewSD, _
SECURITY_DESCRIPTOR_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New Security Descriptor " _ & Err.LastDllError)
Exit Function
End If
'************* End Initialize a new Security Descriptor
***************

'**************** Set the Security Descriptor Dacl
********************
' Set the file's Security Descriptor to the new DACL.
bSuccess = SetSecurityDescriptorDacl(NewSD, 1, pNewACL, 0)
' Make sure you set the SD to the new DACL.
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set New DACL to Security Descriptor")
Exit Function
End If
'************** End Set the Security Descriptor Dacl
******************

'Debug - This is my test function and I get the correct number of
ACEs
GetAceCount(pNewACL)

'********************* Set the File Security
**************************
' The final step is to add the Security Descriptor back to the file bSuccess = SetFileSecurity(sPath, _
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, NewSD)

' Make sure you added the Security Descriptor to the file!
If (bSuccess = 0) Then
MsgBox("Error: Unable to Set New Security Descriptor " _
& " to File : " & sPath)
MsgBox(Err.LastDllError)
Else
MsgBox("Updated Security Descriptor on File: " _
& sPath)
End If
'******************* End Set the File Security
************************

When I put this all together the SetFileSecurity function returns 998 for
LastDllError which seems to be an access denied (?) error. I am an
administrator on the computer where the file resides.

What am I doing wrong now?
Dave

"Imran Koradia" <no****@microsoft.com> wrote in message
news:ud*************@tk2msftngp13.phx.gbl...
Dave,

I'm not too familiar with the security APIs. However, I don't think the
EXPLICIT_ACCESS structure can be used with the AddAce function (Instead,
this structure is used in functions such as SetEntriesInAcl,
GetExplicitEntriesFromAcl, etc). The structure that goes with it are the
various ACE structures (ALLOWED_ACCESS_ACE, etc). However, as I said, I'm not too familar with the security APIs and I could be wrong. I did try using
the ALLOWED_ACCESS_ACE with AddAce and it did seem to work (atleast it
didn't throw up an error). Here's the code that I have:

<StructLayoutAttribute(LayoutKind.Sequential)> _
Public Structure ACL_SIZE_INFORMATION
Public AceCount As Integer
Public AclBytesInUse As Integer
Public AclBytesFree As Integer
End Structure

Public Structure ACE_HEADER
Public AceType As Byte
Public AceFlags As Byte
Public AceSize As Short
End Structure

Public Structure ACCESS_ALLOWED_ACE
Public Header As ACE_HEADER
Public Mask As Integer
Public SidStart As Integer
End Structure

Public Structure ACL
Dim AclRevision As Byte
Dim Sbz1 As Byte
Dim AclSize As Short
Dim AceCount As Short
Dim Sbz2 As Short
End Structure

Declare Function InitializeAcl Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal nAclLength As Integer, _
ByVal dwAclRevision As Integer) As Integer

Declare Function AddAce Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal dwAceRevision As Integer, _
ByVal dwStartingAceIndex As Integer, _
ByVal pAceList As IntPtr, _
ByVal nAceListLength As Integer) As Integer

Declare Auto Function GetAclInformation Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal pAclInformation As IntPtr, _
ByVal AclInformationLength As Integer, _
ByVal dwAclInformationClass As Integer) As Integer
Public Function ReadFileSecurity(ByVal sPath As String) As IntPtr
' from winnt.h
Const SID_MAX_SUB_AUTHORITIES As Integer = 15
Dim SD As IntPtr
' Get the old SD
SD = GetExistingSD(sPath,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION)

' Obtain the DACL.
Dim DACLPresent As Boolean ' Is the DACL present?
Dim Defaulted As Boolean ' Is the DACL defaulted?
Dim DACL As IntPtr ' Pointer to the DACL.

If (GetSecurityDescriptorDacl(SD, DACLPresent, DACL, Defaulted) = 0)
Then
MsgBox("Unable to Get Dacl")
End If

' Obtain the array of ACEs from the DACL.
Dim bSuccess As Integer
Dim ACECount As Integer ' Number of ACEs in

DACL. Dim ACEList As IntPtr ' An array of ACE entries.
Dim sizePtr As IntPtr =
Marshal.AllocHGlobal(Marshal.SizeOf(GetType(ACL_SI ZE_INFORMATION)))
Dim AclInformationLength As Integer =
Marshal.SizeOf(GetType(ACL_SIZE_INFORMATION))
If GetAclInformation(DACL, sizePtr, AclInformationLength, 2) = 0
Then
MsgBox("error: " & Err.LastDllError)
End If

Dim size As Integer = CType(Marshal.PtrToStructure(sizePtr,
GetType(ACL_SIZE_INFORMATION)), ACL_SIZE_INFORMATION).AclBytesInUse

Const ACL_REVISION As Integer = 2
ACECount = 1
Dim pNewACL As IntPtr
Dim newsize As Integer = size +
Marshal.SizeOf(GetType(ACCESS_ALLOWED_ACE)) -
Marshal.SizeOf(GetType(Integer)) + (SID_MAX_SUB_AUTHORITIES *
Marshal.SizeOf(GetType(Integer)))
pNewACL = Marshal.AllocHGlobal(newsize)
bSuccess = 0
bSuccess = InitializeAcl(pNewACL, newsize, ACL_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New ACL")
Exit Function
End If

Dim pAce As IntPtr
bSuccess = GetAce(DACL, 0, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If

Dim AceStruct As ACCESS_ALLOWED_ACE =
CType(Marshal.PtrToStructure(pAce, GetType(ACCESS_ALLOWED_ACE)),
ACCESS_ALLOWED_ACE)

bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
AceStruct.Header.AceSize)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If

End Function
I'm not sure if that's what you are looking for. If you are looking to use the EXPLICIT_ACCESS structure, you might want to look into the functions
that go with it (which I mentioned earlier - they are in the 'See Also'
section in the documentation for the structure).

hope that helps..
Imran.

"Dave Coate" <co*****@comcast.net> wrote in message
news:ed**************************@posting.google.c om...
Imran, Thanks for spotting that (doh!) mistake. I knew that. Just
staring at the code too long. However, after fixing it, I am getting
the same errors on the next function in my code, AddAce(). I double
checked the documentation this time and I believe the function should
be defined with all 5 parameters using ByVal. (The first one is
ambiguous, but since ByRef throws an exception I think I'll go with
ByVal!)

Anyway, here are additions to my code (following the GetAce call) and
the relevant Functions, Structures and Enumerations:

Dave

*************************************************

Const ACL_REVISION = 2
ACECount = 1
Dim pNewACL As IntPtr
bSuccess = InitializeAcl(pNewACL, ACECount *
EXPLICIT_ACCESS.SIZEOF_EA1, ACL_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New ACL")
Exit Function
End If

bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
EXPLICIT_ACCESS.SIZEOF_EA1)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If
Declare Function InitializeAcl Lib "advapi32.dll" ( _
ByRef pAcl As IntPtr, _
ByVal nAclLength As Integer, _
ByVal dwAclRevision As Integer) As Integer

Declare Function AddAce Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal dwAceRevision As Integer, _
ByVal dwStartingAceIndex As Integer, _
ByVal pAceList As IntPtr, _
ByVal nAceListLength As Integer) As Integer

' This data Structure is used to create the explicit entry array.
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure EXPLICIT_ACCESS
Public Shared ReadOnly SIZEOF_EA1 As Integer
Shared Sub New()
SIZEOF_EA1 = Marshal.SizeOf(GetType(EXPLICIT_ACCESS))
End Sub
Public grfAccessPermissions As UInt32
Public grfAccessMode As ACCESS_MODE
Public grfInheritance As UInt32
Public Trustee As Trustee
End Structure

' This Structure contains the trustee information for the ACE.
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure TRUSTEE
Public pMultipleTrustee As IntPtr
Public MultipleTrusteeOperation As MULTIPLE_TRUSTEE_OPERATION
Public TrusteeForm As TRUSTEE_FORM
Public TrusteeType As TRUSTEE_TYPE
Public ptstrName As IntPtr
End Structure

' The ACCESS_MODE enumeration tells what type of ACE entry we're
' working with.
Public Enum ACCESS_MODE
NOT_USED_ACCESS = 0
GRANT_ACCESS
SET_ACCESS
DENY_ACCESS
REVOKE_ACCESS
SET_AUDIT_SUCCESS
SET_AUDIT_FAILURE
End Enum

' The MULTIPLE_TRUSTEE_OPERATION enumeration determines if this
' is a single or a multiple trustee.
Public Enum MULTIPLE_TRUSTEE_OPERATION
NO_MULTIPLE_TRUSTEE
TRUSTEE_IS_IMPERSONATE
End Enum

' The TRUSTEE_FORM enumeration determines what form the ACE
trustee
' takes.
Public Enum TRUSTEE_FORM
TRUSTEE_IS_SID
TRUSTEE_IS_NAME
TRUSTEE_BAD_FORM
TRUSTEE_IS_OBJECTS_AND_SID
TRUSTEE_IS_OBJECTS_AND_NAME
End Enum

' The TRUSTEE_TYPE enumeration determins the type of the trustee.
Public Enum TRUSTEE_TYPE
TRUSTEE_IS_UNKNOWN
TRUSTEE_IS_USER
TRUSTEE_IS_GROUP
TRUSTEE_IS_DOMAIN
TRUSTEE_IS_ALIAS
TRUSTEE_IS_WELL_KNOWN_GROUP
TRUSTEE_IS_DELETED
TRUSTEE_IS_INVALID
TRUSTEE_IS_COMPUTER
End Enum

"Imran Koradia" <no****@microsoft.com> wrote in message

news:<ed**************@TK2MSFTNGP10.phx.gbl>...
> > Declare Function GetAce Lib "advapi32.dll" ( _
> > ByRef pAcl As IntPtr, _
> > ByVal dwAceIndex As Integer, _
> > ByRef pAce As IntPtr) As Boolean
>
> Change pAcl from 'ByRef' to 'ByVal'.
>
> hope that helps..
> Imran.



Nov 21 '05 #6
Apologies. I'll get back soon..

Imran.

"Dave Coate" <co*****@comcast.net> wrote in message
news:eV**************@TK2MSFTNGP10.phx.gbl...
Imran,

If you are still watching this thread, please disregard my last post. I took (yet another) look at the documentation and noticed that
'SetFileSecurity' is now 'obsolete'. I used SetNamedSecurityInfo instead and a lot of things are falling into place. There seems to be multiple API
functions (2 sets?) for working with Security. I am confused about this. I
read somewhere there is a 'High-Level' set of functions and a 'Low-Level'
set of functions. Is this what I am seeing here? Is there a way to know
which is which?

Thanks again,
Dave
"Dave Coate" <co*****@comcast.net> wrote in message
news:uP****************@TK2MSFTNGP12.phx.gbl...
Imran,

Thanks so much for your help so far.

This code seems to run without error for me as well. I am attempting to
understand how to work with file security descriptors. In the simplest case
I want to create a new file security descriptor exactly like the old one

and
place it back on the file. This seems to be accomplished by looping

through
the old SD DACL aces and placing them in a new SD DACL. From there, I

should
be able to selectively exclude some ACEs from the AddAce function (to
effectively remove it from the SD) or add a new ACE using the
AddAccessAllowedAce function after adding all the old ones.

I was able to get a count of the aces in the old DACL from your example

with
the following:
nCLS = CType(Marshal.PtrToStructure(sizePtr,

GetType(ACL_SIZE_INFORMATION)),
ACL_SIZE_INFORMATION).AceCount

Therefore I can use the following Loop:
Dim ix As Integer
For ix = 0 To nACLS - 1

'************************ Get Each Ace
****************************
Dim pAce As IntPtr
bSuccess = GetAce(DACL, ix, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If
'********************** End Get Each Ace
**************************

'************************ Add Each Ace
****************************
Dim AceStruct As ACCESS_ALLOWED_ACE = _
CType(Marshal.PtrToStructure(pAce,
GetType(ACCESS_ALLOWED_ACE)), _
ACCESS_ALLOWED_ACE)

bSuccess = AddAce(pNewACL, ACL_REVISION, ix, pAce, _
AceStruct.Header.AceSize)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If
'********************** End Get Each Ace
**************************
Next

I can confirm that this works by building a function that gets the Ace

Count
of an ACL. By checking this count on both the (original) DACL and the

(new)
pNewACL I can see this number go up and down as I add and subtract users
with access to the file through the expolorer interface. So, I think All I
have left to do is to initialize a new Security Descriptor, set the new

DACL
to this Security Descriptor and set this SD to the file. I have the
following code:

Private Declare Function InitializeSecurityDescriptor Lib

"advapi32.dll"
( _
ByRef pSecurityDescriptor As IntPtr, _
ByVal dwRevision As Integer) As Boolean

Declare Function SetSecurityDescriptorDacl Lib "advapi32.dll" ( _
ByRef pSecurityDescriptor As IntPtr, _
ByVal bDaclPresent As Integer, _
ByVal pDacl As IntPtr, _
ByVal bDaclDefaulted As Integer) As Integer

Declare Function SetFileSecurity Lib "advapi32.dll" Alias
"SetFileSecurityA" ( _
ByVal lpFileName As String, _
ByVal SecurityInformation As Integer, _
ByVal pSecurityDescriptor As IntPtr) As Integer

'************* Initialize a new Security Descriptor
*******************
' Call InitializeSecurityDescriptor to build a new SD for the

file.
Dim NewSD As IntPtr

'Set the size of the new sd to SecDescSize"
'SDSizeNeeded comes from the initial call to GetFileSecurity
'Therefore I am setting the new SD to the size of the old SD for
this experiment
NewSD = Marshal.AllocHGlobal(SDSizeNeeded)

bSuccess = InitializeSecurityDescriptor(NewSD, _
SECURITY_DESCRIPTOR_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New Security Descriptor " _
& Err.LastDllError)
Exit Function
End If
'************* End Initialize a new Security Descriptor
***************

'**************** Set the Security Descriptor Dacl
********************
' Set the file's Security Descriptor to the new DACL.
bSuccess = SetSecurityDescriptorDacl(NewSD, 1, pNewACL, 0)
' Make sure you set the SD to the new DACL.
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set New DACL to Security Descriptor")
Exit Function
End If
'************** End Set the Security Descriptor Dacl
******************

'Debug - This is my test function and I get the correct number
of ACEs
GetAceCount(pNewACL)

'********************* Set the File Security
**************************
' The final step is to add the Security Descriptor back to the

file
bSuccess = SetFileSecurity(sPath, _
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, NewSD)

' Make sure you added the Security Descriptor to the file!
If (bSuccess = 0) Then
MsgBox("Error: Unable to Set New Security Descriptor " _
& " to File : " & sPath)
MsgBox(Err.LastDllError)
Else
MsgBox("Updated Security Descriptor on File: " _
& sPath)
End If
'******************* End Set the File Security
************************

When I put this all together the SetFileSecurity function returns 998 for LastDllError which seems to be an access denied (?) error. I am an
administrator on the computer where the file resides.

What am I doing wrong now?
Dave

"Imran Koradia" <no****@microsoft.com> wrote in message
news:ud*************@tk2msftngp13.phx.gbl...
Dave,

I'm not too familiar with the security APIs. However, I don't think the EXPLICIT_ACCESS structure can be used with the AddAce function (Instead, this structure is used in functions such as SetEntriesInAcl,
GetExplicitEntriesFromAcl, etc). The structure that goes with it are the various ACE structures (ALLOWED_ACCESS_ACE, etc). However, as I said,

I'm not too familar with the security APIs and I could be wrong. I did try

using
the ALLOWED_ACCESS_ACE with AddAce and it did seem to work (atleast it
didn't throw up an error). Here's the code that I have:

<StructLayoutAttribute(LayoutKind.Sequential)> _
Public Structure ACL_SIZE_INFORMATION
Public AceCount As Integer
Public AclBytesInUse As Integer
Public AclBytesFree As Integer
End Structure

Public Structure ACE_HEADER
Public AceType As Byte
Public AceFlags As Byte
Public AceSize As Short
End Structure

Public Structure ACCESS_ALLOWED_ACE
Public Header As ACE_HEADER
Public Mask As Integer
Public SidStart As Integer
End Structure

Public Structure ACL
Dim AclRevision As Byte
Dim Sbz1 As Byte
Dim AclSize As Short
Dim AceCount As Short
Dim Sbz2 As Short
End Structure

Declare Function InitializeAcl Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal nAclLength As Integer, _
ByVal dwAclRevision As Integer) As Integer

Declare Function AddAce Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal dwAceRevision As Integer, _
ByVal dwStartingAceIndex As Integer, _
ByVal pAceList As IntPtr, _
ByVal nAceListLength As Integer) As Integer

Declare Auto Function GetAclInformation Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal pAclInformation As IntPtr, _
ByVal AclInformationLength As Integer, _
ByVal dwAclInformationClass As Integer) As Integer
Public Function ReadFileSecurity(ByVal sPath As String) As IntPtr
' from winnt.h
Const SID_MAX_SUB_AUTHORITIES As Integer = 15
Dim SD As IntPtr
' Get the old SD
SD = GetExistingSD(sPath,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION)

' Obtain the DACL.
Dim DACLPresent As Boolean ' Is the DACL present?
Dim Defaulted As Boolean ' Is the DACL defaulted? Dim DACL As IntPtr ' Pointer to the DACL.

If (GetSecurityDescriptorDacl(SD, DACLPresent, DACL, Defaulted) =
0)
Then
MsgBox("Unable to Get Dacl")
End If

' Obtain the array of ACEs from the DACL.
Dim bSuccess As Integer
Dim ACECount As Integer ' Number of ACEs in DACL. Dim ACEList As IntPtr ' An array of ACE entries.
Dim sizePtr As IntPtr =
Marshal.AllocHGlobal(Marshal.SizeOf(GetType(ACL_SI ZE_INFORMATION)))
Dim AclInformationLength As Integer =
Marshal.SizeOf(GetType(ACL_SIZE_INFORMATION))
If GetAclInformation(DACL, sizePtr, AclInformationLength, 2) =
0 Then
MsgBox("error: " & Err.LastDllError)
End If

Dim size As Integer = CType(Marshal.PtrToStructure(sizePtr,
GetType(ACL_SIZE_INFORMATION)), ACL_SIZE_INFORMATION).AclBytesInUse

Const ACL_REVISION As Integer = 2
ACECount = 1
Dim pNewACL As IntPtr
Dim newsize As Integer = size +
Marshal.SizeOf(GetType(ACCESS_ALLOWED_ACE)) -
Marshal.SizeOf(GetType(Integer)) + (SID_MAX_SUB_AUTHORITIES *
Marshal.SizeOf(GetType(Integer)))
pNewACL = Marshal.AllocHGlobal(newsize)
bSuccess = 0
bSuccess = InitializeAcl(pNewACL, newsize, ACL_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New ACL")
Exit Function
End If

Dim pAce As IntPtr
bSuccess = GetAce(DACL, 0, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If

Dim AceStruct As ACCESS_ALLOWED_ACE =
CType(Marshal.PtrToStructure(pAce, GetType(ACCESS_ALLOWED_ACE)),
ACCESS_ALLOWED_ACE)

bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
AceStruct.Header.AceSize)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If

End Function
I'm not sure if that's what you are looking for. If you are looking to

use the EXPLICIT_ACCESS structure, you might want to look into the functions that go with it (which I mentioned earlier - they are in the 'See Also' section in the documentation for the structure).

hope that helps..
Imran.

"Dave Coate" <co*****@comcast.net> wrote in message
news:ed**************************@posting.google.c om...
> Imran, Thanks for spotting that (doh!) mistake. I knew that. Just
> staring at the code too long. However, after fixing it, I am getting
> the same errors on the next function in my code, AddAce(). I double
> checked the documentation this time and I believe the function should > be defined with all 5 parameters using ByVal. (The first one is
> ambiguous, but since ByRef throws an exception I think I'll go with
> ByVal!)
>
> Anyway, here are additions to my code (following the GetAce call) and > the relevant Functions, Structures and Enumerations:
>
> Dave
>
> *************************************************
>
> Const ACL_REVISION = 2
> ACECount = 1
> Dim pNewACL As IntPtr
> bSuccess = InitializeAcl(pNewACL, ACECount *
> EXPLICIT_ACCESS.SIZEOF_EA1, ACL_REVISION)
> ' A return code of zero means the call failed; test for this
> ' before continuing.
> If (bSuccess = 0) Then
> MsgBox("Error: Unable to Initialize New ACL")
> Exit Function
> End If
>
> bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
> EXPLICIT_ACCESS.SIZEOF_EA1)
> If (bSuccess = 0) Then
> MsgBox("Error: " & _
> "Unable to Set Ace ")
> MsgBox(Err.LastDllError)
> 'Exit Function
> End If
>
>
> Declare Function InitializeAcl Lib "advapi32.dll" ( _
> ByRef pAcl As IntPtr, _
> ByVal nAclLength As Integer, _
> ByVal dwAclRevision As Integer) As Integer
>
> Declare Function AddAce Lib "advapi32.dll" ( _
> ByVal pAcl As IntPtr, _
> ByVal dwAceRevision As Integer, _
> ByVal dwStartingAceIndex As Integer, _
> ByVal pAceList As IntPtr, _
> ByVal nAceListLength As Integer) As Integer
>
> ' This data Structure is used to create the explicit entry array. > <StructLayout(LayoutKind.Sequential, Pack:=1)> _
> Public Structure EXPLICIT_ACCESS
> Public Shared ReadOnly SIZEOF_EA1 As Integer
> Shared Sub New()
> SIZEOF_EA1 = Marshal.SizeOf(GetType(EXPLICIT_ACCESS))
> End Sub
> Public grfAccessPermissions As UInt32
> Public grfAccessMode As ACCESS_MODE
> Public grfInheritance As UInt32
> Public Trustee As Trustee
> End Structure
>
> ' This Structure contains the trustee information for the ACE.
> <StructLayout(LayoutKind.Sequential, Pack:=1)> _
> Public Structure TRUSTEE
> Public pMultipleTrustee As IntPtr
> Public MultipleTrusteeOperation As MULTIPLE_TRUSTEE_OPERATION > Public TrusteeForm As TRUSTEE_FORM
> Public TrusteeType As TRUSTEE_TYPE
> Public ptstrName As IntPtr
> End Structure
>
> ' The ACCESS_MODE enumeration tells what type of ACE entry we're
> ' working with.
> Public Enum ACCESS_MODE
> NOT_USED_ACCESS = 0
> GRANT_ACCESS
> SET_ACCESS
> DENY_ACCESS
> REVOKE_ACCESS
> SET_AUDIT_SUCCESS
> SET_AUDIT_FAILURE
> End Enum
>
> ' The MULTIPLE_TRUSTEE_OPERATION enumeration determines if this
> ' is a single or a multiple trustee.
> Public Enum MULTIPLE_TRUSTEE_OPERATION
> NO_MULTIPLE_TRUSTEE
> TRUSTEE_IS_IMPERSONATE
> End Enum
>
> ' The TRUSTEE_FORM enumeration determines what form the ACE
> trustee
> ' takes.
> Public Enum TRUSTEE_FORM
> TRUSTEE_IS_SID
> TRUSTEE_IS_NAME
> TRUSTEE_BAD_FORM
> TRUSTEE_IS_OBJECTS_AND_SID
> TRUSTEE_IS_OBJECTS_AND_NAME
> End Enum
>
> ' The TRUSTEE_TYPE enumeration determins the type of the trustee. > Public Enum TRUSTEE_TYPE
> TRUSTEE_IS_UNKNOWN
> TRUSTEE_IS_USER
> TRUSTEE_IS_GROUP
> TRUSTEE_IS_DOMAIN
> TRUSTEE_IS_ALIAS
> TRUSTEE_IS_WELL_KNOWN_GROUP
> TRUSTEE_IS_DELETED
> TRUSTEE_IS_INVALID
> TRUSTEE_IS_COMPUTER
> End Enum
>
>
>
> "Imran Koradia" <no****@microsoft.com> wrote in message
news:<ed**************@TK2MSFTNGP10.phx.gbl>...
> > > Declare Function GetAce Lib "advapi32.dll" ( _
> > > ByRef pAcl As IntPtr, _
> > > ByVal dwAceIndex As Integer, _
> > > ByRef pAce As IntPtr) As Boolean
> >
> > Change pAcl from 'ByRef' to 'ByVal'.
> >
> > hope that helps..
> > Imran.



Nov 21 '05 #7
Dave,

I believe you're right. Here's a link to the low-level security APIs:
http://msdn.microsoft.com/library/de..._functions.asp

The others are high-level functions - or rather, not low-level functions.
So, SetFileSecurity is a low-level API while SetNameSecurityInfo is a
high-level API. I'm not sure if one is prefered over the other since, as I
mentioned earlier, I'm not too familiar with security APIs.

hope that helps..
Imran.

"Dave Coate" <co*****@comcast.net> wrote in message
news:eV**************@TK2MSFTNGP10.phx.gbl...
Imran,

If you are still watching this thread, please disregard my last post. I took (yet another) look at the documentation and noticed that
'SetFileSecurity' is now 'obsolete'. I used SetNamedSecurityInfo instead and a lot of things are falling into place. There seems to be multiple API
functions (2 sets?) for working with Security. I am confused about this. I
read somewhere there is a 'High-Level' set of functions and a 'Low-Level'
set of functions. Is this what I am seeing here? Is there a way to know
which is which?

Thanks again,
Dave
"Dave Coate" <co*****@comcast.net> wrote in message
news:uP****************@TK2MSFTNGP12.phx.gbl...
Imran,

Thanks so much for your help so far.

This code seems to run without error for me as well. I am attempting to
understand how to work with file security descriptors. In the simplest case
I want to create a new file security descriptor exactly like the old one

and
place it back on the file. This seems to be accomplished by looping

through
the old SD DACL aces and placing them in a new SD DACL. From there, I

should
be able to selectively exclude some ACEs from the AddAce function (to
effectively remove it from the SD) or add a new ACE using the
AddAccessAllowedAce function after adding all the old ones.

I was able to get a count of the aces in the old DACL from your example

with
the following:
nCLS = CType(Marshal.PtrToStructure(sizePtr,

GetType(ACL_SIZE_INFORMATION)),
ACL_SIZE_INFORMATION).AceCount

Therefore I can use the following Loop:
Dim ix As Integer
For ix = 0 To nACLS - 1

'************************ Get Each Ace
****************************
Dim pAce As IntPtr
bSuccess = GetAce(DACL, ix, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If
'********************** End Get Each Ace
**************************

'************************ Add Each Ace
****************************
Dim AceStruct As ACCESS_ALLOWED_ACE = _
CType(Marshal.PtrToStructure(pAce,
GetType(ACCESS_ALLOWED_ACE)), _
ACCESS_ALLOWED_ACE)

bSuccess = AddAce(pNewACL, ACL_REVISION, ix, pAce, _
AceStruct.Header.AceSize)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If
'********************** End Get Each Ace
**************************
Next

I can confirm that this works by building a function that gets the Ace

Count
of an ACL. By checking this count on both the (original) DACL and the

(new)
pNewACL I can see this number go up and down as I add and subtract users
with access to the file through the expolorer interface. So, I think All I
have left to do is to initialize a new Security Descriptor, set the new

DACL
to this Security Descriptor and set this SD to the file. I have the
following code:

Private Declare Function InitializeSecurityDescriptor Lib

"advapi32.dll"
( _
ByRef pSecurityDescriptor As IntPtr, _
ByVal dwRevision As Integer) As Boolean

Declare Function SetSecurityDescriptorDacl Lib "advapi32.dll" ( _
ByRef pSecurityDescriptor As IntPtr, _
ByVal bDaclPresent As Integer, _
ByVal pDacl As IntPtr, _
ByVal bDaclDefaulted As Integer) As Integer

Declare Function SetFileSecurity Lib "advapi32.dll" Alias
"SetFileSecurityA" ( _
ByVal lpFileName As String, _
ByVal SecurityInformation As Integer, _
ByVal pSecurityDescriptor As IntPtr) As Integer

'************* Initialize a new Security Descriptor
*******************
' Call InitializeSecurityDescriptor to build a new SD for the

file.
Dim NewSD As IntPtr

'Set the size of the new sd to SecDescSize"
'SDSizeNeeded comes from the initial call to GetFileSecurity
'Therefore I am setting the new SD to the size of the old SD for
this experiment
NewSD = Marshal.AllocHGlobal(SDSizeNeeded)

bSuccess = InitializeSecurityDescriptor(NewSD, _
SECURITY_DESCRIPTOR_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New Security Descriptor " _
& Err.LastDllError)
Exit Function
End If
'************* End Initialize a new Security Descriptor
***************

'**************** Set the Security Descriptor Dacl
********************
' Set the file's Security Descriptor to the new DACL.
bSuccess = SetSecurityDescriptorDacl(NewSD, 1, pNewACL, 0)
' Make sure you set the SD to the new DACL.
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set New DACL to Security Descriptor")
Exit Function
End If
'************** End Set the Security Descriptor Dacl
******************

'Debug - This is my test function and I get the correct number
of ACEs
GetAceCount(pNewACL)

'********************* Set the File Security
**************************
' The final step is to add the Security Descriptor back to the

file
bSuccess = SetFileSecurity(sPath, _
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, NewSD)

' Make sure you added the Security Descriptor to the file!
If (bSuccess = 0) Then
MsgBox("Error: Unable to Set New Security Descriptor " _
& " to File : " & sPath)
MsgBox(Err.LastDllError)
Else
MsgBox("Updated Security Descriptor on File: " _
& sPath)
End If
'******************* End Set the File Security
************************

When I put this all together the SetFileSecurity function returns 998 for LastDllError which seems to be an access denied (?) error. I am an
administrator on the computer where the file resides.

What am I doing wrong now?
Dave

"Imran Koradia" <no****@microsoft.com> wrote in message
news:ud*************@tk2msftngp13.phx.gbl...
Dave,

I'm not too familiar with the security APIs. However, I don't think the EXPLICIT_ACCESS structure can be used with the AddAce function (Instead, this structure is used in functions such as SetEntriesInAcl,
GetExplicitEntriesFromAcl, etc). The structure that goes with it are the various ACE structures (ALLOWED_ACCESS_ACE, etc). However, as I said,

I'm not too familar with the security APIs and I could be wrong. I did try

using
the ALLOWED_ACCESS_ACE with AddAce and it did seem to work (atleast it
didn't throw up an error). Here's the code that I have:

<StructLayoutAttribute(LayoutKind.Sequential)> _
Public Structure ACL_SIZE_INFORMATION
Public AceCount As Integer
Public AclBytesInUse As Integer
Public AclBytesFree As Integer
End Structure

Public Structure ACE_HEADER
Public AceType As Byte
Public AceFlags As Byte
Public AceSize As Short
End Structure

Public Structure ACCESS_ALLOWED_ACE
Public Header As ACE_HEADER
Public Mask As Integer
Public SidStart As Integer
End Structure

Public Structure ACL
Dim AclRevision As Byte
Dim Sbz1 As Byte
Dim AclSize As Short
Dim AceCount As Short
Dim Sbz2 As Short
End Structure

Declare Function InitializeAcl Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal nAclLength As Integer, _
ByVal dwAclRevision As Integer) As Integer

Declare Function AddAce Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal dwAceRevision As Integer, _
ByVal dwStartingAceIndex As Integer, _
ByVal pAceList As IntPtr, _
ByVal nAceListLength As Integer) As Integer

Declare Auto Function GetAclInformation Lib "advapi32.dll" ( _
ByVal pAcl As IntPtr, _
ByVal pAclInformation As IntPtr, _
ByVal AclInformationLength As Integer, _
ByVal dwAclInformationClass As Integer) As Integer
Public Function ReadFileSecurity(ByVal sPath As String) As IntPtr
' from winnt.h
Const SID_MAX_SUB_AUTHORITIES As Integer = 15
Dim SD As IntPtr
' Get the old SD
SD = GetExistingSD(sPath,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION)

' Obtain the DACL.
Dim DACLPresent As Boolean ' Is the DACL present?
Dim Defaulted As Boolean ' Is the DACL defaulted? Dim DACL As IntPtr ' Pointer to the DACL.

If (GetSecurityDescriptorDacl(SD, DACLPresent, DACL, Defaulted) =
0)
Then
MsgBox("Unable to Get Dacl")
End If

' Obtain the array of ACEs from the DACL.
Dim bSuccess As Integer
Dim ACECount As Integer ' Number of ACEs in DACL. Dim ACEList As IntPtr ' An array of ACE entries.
Dim sizePtr As IntPtr =
Marshal.AllocHGlobal(Marshal.SizeOf(GetType(ACL_SI ZE_INFORMATION)))
Dim AclInformationLength As Integer =
Marshal.SizeOf(GetType(ACL_SIZE_INFORMATION))
If GetAclInformation(DACL, sizePtr, AclInformationLength, 2) =
0 Then
MsgBox("error: " & Err.LastDllError)
End If

Dim size As Integer = CType(Marshal.PtrToStructure(sizePtr,
GetType(ACL_SIZE_INFORMATION)), ACL_SIZE_INFORMATION).AclBytesInUse

Const ACL_REVISION As Integer = 2
ACECount = 1
Dim pNewACL As IntPtr
Dim newsize As Integer = size +
Marshal.SizeOf(GetType(ACCESS_ALLOWED_ACE)) -
Marshal.SizeOf(GetType(Integer)) + (SID_MAX_SUB_AUTHORITIES *
Marshal.SizeOf(GetType(Integer)))
pNewACL = Marshal.AllocHGlobal(newsize)
bSuccess = 0
bSuccess = InitializeAcl(pNewACL, newsize, ACL_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New ACL")
Exit Function
End If

Dim pAce As IntPtr
bSuccess = GetAce(DACL, 0, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If

Dim AceStruct As ACCESS_ALLOWED_ACE =
CType(Marshal.PtrToStructure(pAce, GetType(ACCESS_ALLOWED_ACE)),
ACCESS_ALLOWED_ACE)

bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
AceStruct.Header.AceSize)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If

End Function
I'm not sure if that's what you are looking for. If you are looking to

use the EXPLICIT_ACCESS structure, you might want to look into the functions that go with it (which I mentioned earlier - they are in the 'See Also' section in the documentation for the structure).

hope that helps..
Imran.

"Dave Coate" <co*****@comcast.net> wrote in message
news:ed**************************@posting.google.c om...
> Imran, Thanks for spotting that (doh!) mistake. I knew that. Just
> staring at the code too long. However, after fixing it, I am getting
> the same errors on the next function in my code, AddAce(). I double
> checked the documentation this time and I believe the function should > be defined with all 5 parameters using ByVal. (The first one is
> ambiguous, but since ByRef throws an exception I think I'll go with
> ByVal!)
>
> Anyway, here are additions to my code (following the GetAce call) and > the relevant Functions, Structures and Enumerations:
>
> Dave
>
> *************************************************
>
> Const ACL_REVISION = 2
> ACECount = 1
> Dim pNewACL As IntPtr
> bSuccess = InitializeAcl(pNewACL, ACECount *
> EXPLICIT_ACCESS.SIZEOF_EA1, ACL_REVISION)
> ' A return code of zero means the call failed; test for this
> ' before continuing.
> If (bSuccess = 0) Then
> MsgBox("Error: Unable to Initialize New ACL")
> Exit Function
> End If
>
> bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
> EXPLICIT_ACCESS.SIZEOF_EA1)
> If (bSuccess = 0) Then
> MsgBox("Error: " & _
> "Unable to Set Ace ")
> MsgBox(Err.LastDllError)
> 'Exit Function
> End If
>
>
> Declare Function InitializeAcl Lib "advapi32.dll" ( _
> ByRef pAcl As IntPtr, _
> ByVal nAclLength As Integer, _
> ByVal dwAclRevision As Integer) As Integer
>
> Declare Function AddAce Lib "advapi32.dll" ( _
> ByVal pAcl As IntPtr, _
> ByVal dwAceRevision As Integer, _
> ByVal dwStartingAceIndex As Integer, _
> ByVal pAceList As IntPtr, _
> ByVal nAceListLength As Integer) As Integer
>
> ' This data Structure is used to create the explicit entry array. > <StructLayout(LayoutKind.Sequential, Pack:=1)> _
> Public Structure EXPLICIT_ACCESS
> Public Shared ReadOnly SIZEOF_EA1 As Integer
> Shared Sub New()
> SIZEOF_EA1 = Marshal.SizeOf(GetType(EXPLICIT_ACCESS))
> End Sub
> Public grfAccessPermissions As UInt32
> Public grfAccessMode As ACCESS_MODE
> Public grfInheritance As UInt32
> Public Trustee As Trustee
> End Structure
>
> ' This Structure contains the trustee information for the ACE.
> <StructLayout(LayoutKind.Sequential, Pack:=1)> _
> Public Structure TRUSTEE
> Public pMultipleTrustee As IntPtr
> Public MultipleTrusteeOperation As MULTIPLE_TRUSTEE_OPERATION > Public TrusteeForm As TRUSTEE_FORM
> Public TrusteeType As TRUSTEE_TYPE
> Public ptstrName As IntPtr
> End Structure
>
> ' The ACCESS_MODE enumeration tells what type of ACE entry we're
> ' working with.
> Public Enum ACCESS_MODE
> NOT_USED_ACCESS = 0
> GRANT_ACCESS
> SET_ACCESS
> DENY_ACCESS
> REVOKE_ACCESS
> SET_AUDIT_SUCCESS
> SET_AUDIT_FAILURE
> End Enum
>
> ' The MULTIPLE_TRUSTEE_OPERATION enumeration determines if this
> ' is a single or a multiple trustee.
> Public Enum MULTIPLE_TRUSTEE_OPERATION
> NO_MULTIPLE_TRUSTEE
> TRUSTEE_IS_IMPERSONATE
> End Enum
>
> ' The TRUSTEE_FORM enumeration determines what form the ACE
> trustee
> ' takes.
> Public Enum TRUSTEE_FORM
> TRUSTEE_IS_SID
> TRUSTEE_IS_NAME
> TRUSTEE_BAD_FORM
> TRUSTEE_IS_OBJECTS_AND_SID
> TRUSTEE_IS_OBJECTS_AND_NAME
> End Enum
>
> ' The TRUSTEE_TYPE enumeration determins the type of the trustee. > Public Enum TRUSTEE_TYPE
> TRUSTEE_IS_UNKNOWN
> TRUSTEE_IS_USER
> TRUSTEE_IS_GROUP
> TRUSTEE_IS_DOMAIN
> TRUSTEE_IS_ALIAS
> TRUSTEE_IS_WELL_KNOWN_GROUP
> TRUSTEE_IS_DELETED
> TRUSTEE_IS_INVALID
> TRUSTEE_IS_COMPUTER
> End Enum
>
>
>
> "Imran Koradia" <no****@microsoft.com> wrote in message
news:<ed**************@TK2MSFTNGP10.phx.gbl>...
> > > Declare Function GetAce Lib "advapi32.dll" ( _
> > > ByRef pAcl As IntPtr, _
> > > ByVal dwAceIndex As Integer, _
> > > ByRef pAce As IntPtr) As Boolean
> >
> > Change pAcl from 'ByRef' to 'ByVal'.
> >
> > hope that helps..
> > Imran.



Nov 21 '05 #8
Imran,

It is all starting to become clear now. Since I changed my code to use
SetNamedSecurityInfo I have successfully:

1) Read the Owner and Dacl (and all the Aces inside) for a file
2) Created a new Dacl and filled it with the Aces from the original Dacl
(A seemingly useless excersize except that it is necessary for the next
step...)
3) Created a new Dacl with the Aces from the old Dacl plus a new (Access
Allowed) Ace. This effectively adds a new user or group access to the file
4) Deleted an Ace using DeleteAce()
5) Changed the owner of a file

I think I can now code everything I want to do using these 'High-Level'
functions. I found the link to the 'Low-Level' functions last night. Do you
know if there is a list anywhere of the High-Level functions? It appears
that Microsoft is moving toward the High-Level functions. The MSDN page for
SetFileSecurity indicates that it is 'Obsolete'.
http://msdn.microsoft.com/library/de...lesecurity.asp
Given that and the fact that I can make the High-Level functions work, I
think I will stick with them.

One other question: Many of the examples I have encountered show code for
freeing memory after using a particular API function. I see the command
'Marshal.FreeHGlobal()' used a lot in this context. Can you give me a quick
overview of how to know when this is necessary?

Thank you so much, I am almost there!
Dave
"Imran Koradia" <no****@microsoft.com> wrote in message
news:e3**************@TK2MSFTNGP10.phx.gbl...
Dave,

I believe you're right. Here's a link to the low-level security APIs:
http://msdn.microsoft.com/library/de..._functions.asp
The others are high-level functions - or rather, not low-level functions.
So, SetFileSecurity is a low-level API while SetNameSecurityInfo is a
high-level API. I'm not sure if one is prefered over the other since, as I
mentioned earlier, I'm not too familiar with security APIs.

hope that helps..
Imran.

"Dave Coate" <co*****@comcast.net> wrote in message
news:eV**************@TK2MSFTNGP10.phx.gbl...
Imran,

If you are still watching this thread, please disregard my last post.
I
took (yet another) look at the documentation and noticed that
'SetFileSecurity' is now 'obsolete'. I used SetNamedSecurityInfo instead and
a lot of things are falling into place. There seems to be multiple API
functions (2 sets?) for working with Security. I am confused about this. I read somewhere there is a 'High-Level' set of functions and a 'Low-Level' set of functions. Is this what I am seeing here? Is there a way to know
which is which?

Thanks again,
Dave
"Dave Coate" <co*****@comcast.net> wrote in message
news:uP****************@TK2MSFTNGP12.phx.gbl...
Imran,

Thanks so much for your help so far.

This code seems to run without error for me as well. I am attempting to understand how to work with file security descriptors. In the simplest

case
I want to create a new file security descriptor exactly like the old one
and
place it back on the file. This seems to be accomplished by looping

through
the old SD DACL aces and placing them in a new SD DACL. From there, I

should
be able to selectively exclude some ACEs from the AddAce function (to
effectively remove it from the SD) or add a new ACE using the
AddAccessAllowedAce function after adding all the old ones.

I was able to get a count of the aces in the old DACL from your
example with
the following:
nCLS = CType(Marshal.PtrToStructure(sizePtr,

GetType(ACL_SIZE_INFORMATION)),
ACL_SIZE_INFORMATION).AceCount

Therefore I can use the following Loop:
Dim ix As Integer
For ix = 0 To nACLS - 1

'************************ Get Each Ace
****************************
Dim pAce As IntPtr
bSuccess = GetAce(DACL, ix, pAce)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Get Ace")
MsgBox(Err.LastDllError)
End If
'********************** End Get Each Ace
**************************

'************************ Add Each Ace
****************************
Dim AceStruct As ACCESS_ALLOWED_ACE = _
CType(Marshal.PtrToStructure(pAce,
GetType(ACCESS_ALLOWED_ACE)), _
ACCESS_ALLOWED_ACE)

bSuccess = AddAce(pNewACL, ACL_REVISION, ix, pAce, _
AceStruct.Header.AceSize)
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set Ace ")
MsgBox(Err.LastDllError)
'Exit Function
End If
'********************** End Get Each Ace
**************************
Next

I can confirm that this works by building a function that gets the Ace

Count
of an ACL. By checking this count on both the (original) DACL and the

(new)
pNewACL I can see this number go up and down as I add and subtract
users with access to the file through the expolorer interface. So, I think All I have left to do is to initialize a new Security Descriptor, set the
new DACL
to this Security Descriptor and set this SD to the file. I have the
following code:

Private Declare Function InitializeSecurityDescriptor Lib "advapi32.dll"
( _
ByRef pSecurityDescriptor As IntPtr, _
ByVal dwRevision As Integer) As Boolean

Declare Function SetSecurityDescriptorDacl Lib "advapi32.dll" ( _
ByRef pSecurityDescriptor As IntPtr, _
ByVal bDaclPresent As Integer, _
ByVal pDacl As IntPtr, _
ByVal bDaclDefaulted As Integer) As Integer

Declare Function SetFileSecurity Lib "advapi32.dll" Alias
"SetFileSecurityA" ( _
ByVal lpFileName As String, _
ByVal SecurityInformation As Integer, _
ByVal pSecurityDescriptor As IntPtr) As Integer

'************* Initialize a new Security Descriptor
*******************
' Call InitializeSecurityDescriptor to build a new SD for the

file.
Dim NewSD As IntPtr

'Set the size of the new sd to SecDescSize"
'SDSizeNeeded comes from the initial call to GetFileSecurity
'Therefore I am setting the new SD to the size of the old SD
for this experiment
NewSD = Marshal.AllocHGlobal(SDSizeNeeded)

bSuccess = InitializeSecurityDescriptor(NewSD, _
SECURITY_DESCRIPTOR_REVISION)
' A return code of zero means the call failed; test for this
' before continuing.
If (bSuccess = 0) Then
MsgBox("Error: Unable to Initialize New Security Descriptor "
_
& Err.LastDllError)
Exit Function
End If
'************* End Initialize a new Security Descriptor
***************

'**************** Set the Security Descriptor Dacl
********************
' Set the file's Security Descriptor to the new DACL.
bSuccess = SetSecurityDescriptorDacl(NewSD, 1, pNewACL, 0)
' Make sure you set the SD to the new DACL.
If (bSuccess = 0) Then
MsgBox("Error: " & _
"Unable to Set New DACL to Security Descriptor")
Exit Function
End If
'************** End Set the Security Descriptor Dacl
******************

'Debug - This is my test function and I get the correct number
of ACEs
GetAceCount(pNewACL)

'********************* Set the File Security
**************************
' The final step is to add the Security Descriptor back to the

file
bSuccess = SetFileSecurity(sPath, _
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, NewSD)

' Make sure you added the Security Descriptor to the file!
If (bSuccess = 0) Then
MsgBox("Error: Unable to Set New Security Descriptor " _
& " to File : " & sPath)
MsgBox(Err.LastDllError)
Else
MsgBox("Updated Security Descriptor on File: " _
& sPath)
End If
'******************* End Set the File Security
************************

When I put this all together the SetFileSecurity function returns 998 for LastDllError which seems to be an access denied (?) error. I am an
administrator on the computer where the file resides.

What am I doing wrong now?
Dave

"Imran Koradia" <no****@microsoft.com> wrote in message
news:ud*************@tk2msftngp13.phx.gbl...
> Dave,
>
> I'm not too familiar with the security APIs. However, I don't think the > EXPLICIT_ACCESS structure can be used with the AddAce function (Instead, > this structure is used in functions such as SetEntriesInAcl,
> GetExplicitEntriesFromAcl, etc). The structure that goes with it are the > various ACE structures (ALLOWED_ACCESS_ACE, etc). However, as I
said, I'm
> not too familar with the security APIs and I could be wrong. I did
try using
> the ALLOWED_ACCESS_ACE with AddAce and it did seem to work (atleast it > didn't throw up an error). Here's the code that I have:
>
> <StructLayoutAttribute(LayoutKind.Sequential)> _
> Public Structure ACL_SIZE_INFORMATION
> Public AceCount As Integer
> Public AclBytesInUse As Integer
> Public AclBytesFree As Integer
> End Structure
>
> Public Structure ACE_HEADER
> Public AceType As Byte
> Public AceFlags As Byte
> Public AceSize As Short
> End Structure
>
> Public Structure ACCESS_ALLOWED_ACE
> Public Header As ACE_HEADER
> Public Mask As Integer
> Public SidStart As Integer
> End Structure
>
> Public Structure ACL
> Dim AclRevision As Byte
> Dim Sbz1 As Byte
> Dim AclSize As Short
> Dim AceCount As Short
> Dim Sbz2 As Short
> End Structure
>
> Declare Function InitializeAcl Lib "advapi32.dll" ( _
> ByVal pAcl As IntPtr, _
> ByVal nAclLength As Integer, _
> ByVal dwAclRevision As Integer) As Integer
>
> Declare Function AddAce Lib "advapi32.dll" ( _
> ByVal pAcl As IntPtr, _
> ByVal dwAceRevision As Integer, _
> ByVal dwStartingAceIndex As Integer, _
> ByVal pAceList As IntPtr, _
> ByVal nAceListLength As Integer) As Integer
>
> Declare Auto Function GetAclInformation Lib "advapi32.dll" ( _
> ByVal pAcl As IntPtr, _
> ByVal pAclInformation As IntPtr, _
> ByVal AclInformationLength As Integer, _
> ByVal dwAclInformationClass As Integer) As Integer
>
>
> Public Function ReadFileSecurity(ByVal sPath As String) As IntPtr
> ' from winnt.h
> Const SID_MAX_SUB_AUTHORITIES As Integer = 15
> Dim SD As IntPtr
> ' Get the old SD
> SD = GetExistingSD(sPath,
> SECURITY_INFORMATION.DACL_SECURITY_INFORMATION)
>
> ' Obtain the DACL.
> Dim DACLPresent As Boolean ' Is the DACL present? > Dim Defaulted As Boolean ' Is the DACL

defaulted? > Dim DACL As IntPtr ' Pointer to the DACL. >
> If (GetSecurityDescriptorDacl(SD, DACLPresent, DACL, Defaulted)
=
0)
> Then
> MsgBox("Unable to Get Dacl")
> End If
>
> ' Obtain the array of ACEs from the DACL.
> Dim bSuccess As Integer
> Dim ACECount As Integer ' Number of ACEs in

DACL.
> Dim ACEList As IntPtr ' An array of ACE

entries.
>
> Dim sizePtr As IntPtr =
> Marshal.AllocHGlobal(Marshal.SizeOf(GetType(ACL_SI ZE_INFORMATION)))
> Dim AclInformationLength As Integer =
> Marshal.SizeOf(GetType(ACL_SIZE_INFORMATION))
> If GetAclInformation(DACL, sizePtr, AclInformationLength, 2) = 0 > Then
> MsgBox("error: " & Err.LastDllError)
> End If
>
> Dim size As Integer = CType(Marshal.PtrToStructure(sizePtr,
> GetType(ACL_SIZE_INFORMATION)), ACL_SIZE_INFORMATION).AclBytesInUse
>
> Const ACL_REVISION As Integer = 2
> ACECount = 1
> Dim pNewACL As IntPtr
> Dim newsize As Integer = size +
> Marshal.SizeOf(GetType(ACCESS_ALLOWED_ACE)) -
> Marshal.SizeOf(GetType(Integer)) + (SID_MAX_SUB_AUTHORITIES *
> Marshal.SizeOf(GetType(Integer)))
> pNewACL = Marshal.AllocHGlobal(newsize)
> bSuccess = 0
> bSuccess = InitializeAcl(pNewACL, newsize, ACL_REVISION)
> ' A return code of zero means the call failed; test for this
> ' before continuing.
> If (bSuccess = 0) Then
> MsgBox("Error: Unable to Initialize New ACL")
> Exit Function
> End If
>
> Dim pAce As IntPtr
> bSuccess = GetAce(DACL, 0, pAce)
> If (bSuccess = 0) Then
> MsgBox("Error: " & _
> "Unable to Get Ace")
> MsgBox(Err.LastDllError)
> End If
>
> Dim AceStruct As ACCESS_ALLOWED_ACE =
> CType(Marshal.PtrToStructure(pAce, GetType(ACCESS_ALLOWED_ACE)),
> ACCESS_ALLOWED_ACE)
>
> bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
> AceStruct.Header.AceSize)
> If (bSuccess = 0) Then
> MsgBox("Error: " & _
> "Unable to Set Ace ")
> MsgBox(Err.LastDllError)
> 'Exit Function
> End If
>
> End Function
>
>
> I'm not sure if that's what you are looking for. If you are looking
to use
> the EXPLICIT_ACCESS structure, you might want to look into the

functions > that go with it (which I mentioned earlier - they are in the 'See Also' > section in the documentation for the structure).
>
> hope that helps..
> Imran.
>
> "Dave Coate" <co*****@comcast.net> wrote in message
> news:ed**************************@posting.google.c om...
> > Imran, Thanks for spotting that (doh!) mistake. I knew that. Just
> > staring at the code too long. However, after fixing it, I am
getting > > the same errors on the next function in my code, AddAce(). I double > > checked the documentation this time and I believe the function

should > > be defined with all 5 parameters using ByVal. (The first one is
> > ambiguous, but since ByRef throws an exception I think I'll go with > > ByVal!)
> >
> > Anyway, here are additions to my code (following the GetAce call) and > > the relevant Functions, Structures and Enumerations:
> >
> > Dave
> >
> > *************************************************
> >
> > Const ACL_REVISION = 2
> > ACECount = 1
> > Dim pNewACL As IntPtr
> > bSuccess = InitializeAcl(pNewACL, ACECount *
> > EXPLICIT_ACCESS.SIZEOF_EA1, ACL_REVISION)
> > ' A return code of zero means the call failed; test for this > > ' before continuing.
> > If (bSuccess = 0) Then
> > MsgBox("Error: Unable to Initialize New ACL")
> > Exit Function
> > End If
> >
> > bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
> > EXPLICIT_ACCESS.SIZEOF_EA1)
> > If (bSuccess = 0) Then
> > MsgBox("Error: " & _
> > "Unable to Set Ace ")
> > MsgBox(Err.LastDllError)
> > 'Exit Function
> > End If
> >
> >
> > Declare Function InitializeAcl Lib "advapi32.dll" ( _
> > ByRef pAcl As IntPtr, _
> > ByVal nAclLength As Integer, _
> > ByVal dwAclRevision As Integer) As Integer
> >
> > Declare Function AddAce Lib "advapi32.dll" ( _
> > ByVal pAcl As IntPtr, _
> > ByVal dwAceRevision As Integer, _
> > ByVal dwStartingAceIndex As Integer, _
> > ByVal pAceList As IntPtr, _
> > ByVal nAceListLength As Integer) As Integer
> >
> > ' This data Structure is used to create the explicit entry array. > > <StructLayout(LayoutKind.Sequential, Pack:=1)> _
> > Public Structure EXPLICIT_ACCESS
> > Public Shared ReadOnly SIZEOF_EA1 As Integer
> > Shared Sub New()
> > SIZEOF_EA1 = Marshal.SizeOf(GetType(EXPLICIT_ACCESS))
> > End Sub
> > Public grfAccessPermissions As UInt32
> > Public grfAccessMode As ACCESS_MODE
> > Public grfInheritance As UInt32
> > Public Trustee As Trustee
> > End Structure
> >
> > ' This Structure contains the trustee information for the ACE.
> > <StructLayout(LayoutKind.Sequential, Pack:=1)> _
> > Public Structure TRUSTEE
> > Public pMultipleTrustee As IntPtr
> > Public MultipleTrusteeOperation As MULTIPLE_TRUSTEE_OPERATION > > Public TrusteeForm As TRUSTEE_FORM
> > Public TrusteeType As TRUSTEE_TYPE
> > Public ptstrName As IntPtr
> > End Structure
> >
> > ' The ACCESS_MODE enumeration tells what type of ACE entry we're > > ' working with.
> > Public Enum ACCESS_MODE
> > NOT_USED_ACCESS = 0
> > GRANT_ACCESS
> > SET_ACCESS
> > DENY_ACCESS
> > REVOKE_ACCESS
> > SET_AUDIT_SUCCESS
> > SET_AUDIT_FAILURE
> > End Enum
> >
> > ' The MULTIPLE_TRUSTEE_OPERATION enumeration determines if this > > ' is a single or a multiple trustee.
> > Public Enum MULTIPLE_TRUSTEE_OPERATION
> > NO_MULTIPLE_TRUSTEE
> > TRUSTEE_IS_IMPERSONATE
> > End Enum
> >
> > ' The TRUSTEE_FORM enumeration determines what form the ACE
> > trustee
> > ' takes.
> > Public Enum TRUSTEE_FORM
> > TRUSTEE_IS_SID
> > TRUSTEE_IS_NAME
> > TRUSTEE_BAD_FORM
> > TRUSTEE_IS_OBJECTS_AND_SID
> > TRUSTEE_IS_OBJECTS_AND_NAME
> > End Enum
> >
> > ' The TRUSTEE_TYPE enumeration determins the type of the trustee. > > Public Enum TRUSTEE_TYPE
> > TRUSTEE_IS_UNKNOWN
> > TRUSTEE_IS_USER
> > TRUSTEE_IS_GROUP
> > TRUSTEE_IS_DOMAIN
> > TRUSTEE_IS_ALIAS
> > TRUSTEE_IS_WELL_KNOWN_GROUP
> > TRUSTEE_IS_DELETED
> > TRUSTEE_IS_INVALID
> > TRUSTEE_IS_COMPUTER
> > End Enum
> >
> >
> >
> > "Imran Koradia" <no****@microsoft.com> wrote in message
> news:<ed**************@TK2MSFTNGP10.phx.gbl>...
> > > > Declare Function GetAce Lib "advapi32.dll" ( _
> > > > ByRef pAcl As IntPtr, _
> > > > ByVal dwAceIndex As Integer, _
> > > > ByRef pAce As IntPtr) As Boolean
> > >
> > > Change pAcl from 'ByRef' to 'ByVal'.
> > >
> > > hope that helps..
> > > Imran.
>
>



Nov 21 '05 #9

"Dave Coate" <co*****@comcast.net> wrote in message
news:O%***************@TK2MSFTNGP09.phx.gbl...
Imran,

It is all starting to become clear now. Since I changed my code to use
SetNamedSecurityInfo I have successfully:

1) Read the Owner and Dacl (and all the Aces inside) for a file
2) Created a new Dacl and filled it with the Aces from the original Dacl
(A seemingly useless excersize except that it is necessary for the next step...)
3) Created a new Dacl with the Aces from the old Dacl plus a new (Access
Allowed) Ace. This effectively adds a new user or group access to the file
4) Deleted an Ace using DeleteAce()
5) Changed the owner of a file

I think I can now code everything I want to do using these 'High-Level'
functions. I found the link to the 'Low-Level' functions last night. Do you know if there is a list anywhere of the High-Level functions? It appears
that Microsoft is moving toward the High-Level functions. The MSDN page for SetFileSecurity indicates that it is 'Obsolete'.
http://msdn.microsoft.com/library/de...lesecurity.asp Given that and the fact that I can make the High-Level functions work, I
think I will stick with them.
good to hear things worked out well :)
One other question: Many of the examples I have encountered show code for
freeing memory after using a particular API function. I see the command
'Marshal.FreeHGlobal()' used a lot in this context. Can you give me a quick overview of how to know when this is necessary?
Whenever you allocate memory using AllocHGlobal you must use FreeHGlobal.
Pass in the IntPtr to FreeHGlobal that is returned by AllocHGlobal and the
runtime will free the memory occupied by the object pointed to by that
pointer. The reason is this: In managed code, everything is allocated on the
managed heap which is looked after by the garbage collector (GC). The GC
usually manages allocations-deallocations of memory on the managed heap -
the programmer does not have (usually) have to do anything for this.
However, whenever one crosses the boundary into unmanaged code (as is the
case when P/Invoking to access Win32 APIs), one needs to be more careful of
allocations-deallocations simply because the GC is not aware of memory
allocations in the unmanaged memory space. The AllocHGlobal allocates space
from the
unmanaged memory and hence one needs to clean it up (just like in the old
C++ days) otherwise it could (and probably would) lead to memory leaks in
your application.

hope that helps..
Imran.
Thank you so much, I am almost there!
Dave
"Imran Koradia" <no****@microsoft.com> wrote in message
news:e3**************@TK2MSFTNGP10.phx.gbl...
Dave,

I believe you're right. Here's a link to the low-level security APIs:

http://msdn.microsoft.com/library/de..._functions.asp

The others are high-level functions - or rather, not low-level functions.
So, SetFileSecurity is a low-level API while SetNameSecurityInfo is a
high-level API. I'm not sure if one is prefered over the other since, as I mentioned earlier, I'm not too familiar with security APIs.

hope that helps..
Imran.

"Dave Coate" <co*****@comcast.net> wrote in message
news:eV**************@TK2MSFTNGP10.phx.gbl...
Imran,

If you are still watching this thread, please disregard my last post.
I
took (yet another) look at the documentation and noticed that
'SetFileSecurity' is now 'obsolete'. I used SetNamedSecurityInfo instead and
a lot of things are falling into place. There seems to be multiple API
functions (2 sets?) for working with Security. I am confused about
this. I read somewhere there is a 'High-Level' set of functions and a 'Low-Level' set of functions. Is this what I am seeing here? Is there a way to
know which is which?

Thanks again,
Dave
"Dave Coate" <co*****@comcast.net> wrote in message
news:uP****************@TK2MSFTNGP12.phx.gbl...
> Imran,
>
> Thanks so much for your help so far.
>
> This code seems to run without error for me as well. I am attempting
to > understand how to work with file security descriptors. In the simplest case
> I want to create a new file security descriptor exactly like the old one and
> place it back on the file. This seems to be accomplished by looping
through
> the old SD DACL aces and placing them in a new SD DACL. From there, I should
> be able to selectively exclude some ACEs from the AddAce function (to > effectively remove it from the SD) or add a new ACE using the
> AddAccessAllowedAce function after adding all the old ones.
>
> I was able to get a count of the aces in the old DACL from your example with
> the following:
> nCLS = CType(Marshal.PtrToStructure(sizePtr,
GetType(ACL_SIZE_INFORMATION)),
> ACL_SIZE_INFORMATION).AceCount
>
> Therefore I can use the following Loop:
> Dim ix As Integer
> For ix = 0 To nACLS - 1
>
> '************************ Get Each Ace
> ****************************
> Dim pAce As IntPtr
> bSuccess = GetAce(DACL, ix, pAce)
> If (bSuccess = 0) Then
> MsgBox("Error: " & _
> "Unable to Get Ace")
> MsgBox(Err.LastDllError)
> End If
> '********************** End Get Each Ace
> **************************
>
> '************************ Add Each Ace
> ****************************
> Dim AceStruct As ACCESS_ALLOWED_ACE = _
> CType(Marshal.PtrToStructure(pAce,
> GetType(ACCESS_ALLOWED_ACE)), _
> ACCESS_ALLOWED_ACE)
>
> bSuccess = AddAce(pNewACL, ACL_REVISION, ix, pAce, _
> AceStruct.Header.AceSize)
> If (bSuccess = 0) Then
> MsgBox("Error: " & _
> "Unable to Set Ace ")
> MsgBox(Err.LastDllError)
> 'Exit Function
> End If
> '********************** End Get Each Ace
> **************************
> Next
>
> I can confirm that this works by building a function that gets the Ace Count
> of an ACL. By checking this count on both the (original) DACL and the (new)
> pNewACL I can see this number go up and down as I add and subtract users > with access to the file through the expolorer interface. So, I think All
I
> have left to do is to initialize a new Security Descriptor, set the

new DACL
> to this Security Descriptor and set this SD to the file. I have the
> following code:
>
> Private Declare Function InitializeSecurityDescriptor Lib
"advapi32.dll"
> ( _
> ByRef pSecurityDescriptor As IntPtr, _
> ByVal dwRevision As Integer) As Boolean
>
> Declare Function SetSecurityDescriptorDacl Lib "advapi32.dll" ( _ > ByRef pSecurityDescriptor As IntPtr, _
> ByVal bDaclPresent As Integer, _
> ByVal pDacl As IntPtr, _
> ByVal bDaclDefaulted As Integer) As Integer
>
> Declare Function SetFileSecurity Lib "advapi32.dll" Alias
> "SetFileSecurityA" ( _
> ByVal lpFileName As String, _
> ByVal SecurityInformation As Integer, _
> ByVal pSecurityDescriptor As IntPtr) As Integer
>
> '************* Initialize a new Security Descriptor
> *******************
> ' Call InitializeSecurityDescriptor to build a new SD for the file.
> Dim NewSD As IntPtr
>
> 'Set the size of the new sd to SecDescSize"
> 'SDSizeNeeded comes from the initial call to GetFileSecurity
> 'Therefore I am setting the new SD to the size of the old SD for > this experiment
> NewSD = Marshal.AllocHGlobal(SDSizeNeeded)
>
> bSuccess = InitializeSecurityDescriptor(NewSD, _
> SECURITY_DESCRIPTOR_REVISION)
> ' A return code of zero means the call failed; test for this
> ' before continuing.
> If (bSuccess = 0) Then
> MsgBox("Error: Unable to Initialize New Security Descriptor
"
_
> & Err.LastDllError)
> Exit Function
> End If
> '************* End Initialize a new Security Descriptor
> ***************
>
> '**************** Set the Security Descriptor Dacl
> ********************
> ' Set the file's Security Descriptor to the new DACL.
> bSuccess = SetSecurityDescriptorDacl(NewSD, 1, pNewACL, 0)
> ' Make sure you set the SD to the new DACL.
> If (bSuccess = 0) Then
> MsgBox("Error: " & _
> "Unable to Set New DACL to Security Descriptor")
> Exit Function
> End If
> '************** End Set the Security Descriptor Dacl
> ******************
>
> 'Debug - This is my test function and I get the correct number of
> ACEs
> GetAceCount(pNewACL)
>
> '********************* Set the File Security
> **************************
> ' The final step is to add the Security Descriptor back to
the file
> bSuccess = SetFileSecurity(sPath, _
> SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, NewSD)
>
> ' Make sure you added the Security Descriptor to the file!
> If (bSuccess = 0) Then
> MsgBox("Error: Unable to Set New Security Descriptor " _
> & " to File : " & sPath)
> MsgBox(Err.LastDllError)
> Else
> MsgBox("Updated Security Descriptor on File: " _
> & sPath)
> End If
> '******************* End Set the File Security
> ************************
>
> When I put this all together the SetFileSecurity function returns 998 for
> LastDllError which seems to be an access denied (?) error. I am an
> administrator on the computer where the file resides.
>
> What am I doing wrong now?
> Dave
>
>
>
> "Imran Koradia" <no****@microsoft.com> wrote in message
> news:ud*************@tk2msftngp13.phx.gbl...
> > Dave,
> >
> > I'm not too familiar with the security APIs. However, I don't
think the
> > EXPLICIT_ACCESS structure can be used with the AddAce function

(Instead,
> > this structure is used in functions such as SetEntriesInAcl,
> > GetExplicitEntriesFromAcl, etc). The structure that goes with it
are the
> > various ACE structures (ALLOWED_ACCESS_ACE, etc). However, as I said, I'm
> > not too familar with the security APIs and I could be wrong. I did try > using
> > the ALLOWED_ACCESS_ACE with AddAce and it did seem to work
(atleast it > > didn't throw up an error). Here's the code that I have:
> >
> > <StructLayoutAttribute(LayoutKind.Sequential)> _
> > Public Structure ACL_SIZE_INFORMATION
> > Public AceCount As Integer
> > Public AclBytesInUse As Integer
> > Public AclBytesFree As Integer
> > End Structure
> >
> > Public Structure ACE_HEADER
> > Public AceType As Byte
> > Public AceFlags As Byte
> > Public AceSize As Short
> > End Structure
> >
> > Public Structure ACCESS_ALLOWED_ACE
> > Public Header As ACE_HEADER
> > Public Mask As Integer
> > Public SidStart As Integer
> > End Structure
> >
> > Public Structure ACL
> > Dim AclRevision As Byte
> > Dim Sbz1 As Byte
> > Dim AclSize As Short
> > Dim AceCount As Short
> > Dim Sbz2 As Short
> > End Structure
> >
> > Declare Function InitializeAcl Lib "advapi32.dll" ( _
> > ByVal pAcl As IntPtr, _
> > ByVal nAclLength As Integer, _
> > ByVal dwAclRevision As Integer) As Integer
> >
> > Declare Function AddAce Lib "advapi32.dll" ( _
> > ByVal pAcl As IntPtr, _
> > ByVal dwAceRevision As Integer, _
> > ByVal dwStartingAceIndex As Integer, _
> > ByVal pAceList As IntPtr, _
> > ByVal nAceListLength As Integer) As Integer
> >
> > Declare Auto Function GetAclInformation Lib "advapi32.dll" ( _
> > ByVal pAcl As IntPtr, _
> > ByVal pAclInformation As IntPtr, _
> > ByVal AclInformationLength As Integer, _
> > ByVal dwAclInformationClass As Integer) As Integer
> >
> >
> > Public Function ReadFileSecurity(ByVal sPath As String) As IntPtr
> > ' from winnt.h
> > Const SID_MAX_SUB_AUTHORITIES As Integer = 15
> > Dim SD As IntPtr
> > ' Get the old SD
> > SD = GetExistingSD(sPath,
> > SECURITY_INFORMATION.DACL_SECURITY_INFORMATION)
> >
> > ' Obtain the DACL.
> > Dim DACLPresent As Boolean ' Is the DACL present? > > Dim Defaulted As Boolean ' Is the DACL defaulted?
> > Dim DACL As IntPtr ' Pointer to the DACL. > >
> > If (GetSecurityDescriptorDacl(SD, DACLPresent, DACL,

Defaulted)
=
> 0)
> > Then
> > MsgBox("Unable to Get Dacl")
> > End If
> >
> > ' Obtain the array of ACEs from the DACL.
> > Dim bSuccess As Integer
> > Dim ACECount As Integer ' Number of ACEs
in DACL.
> > Dim ACEList As IntPtr ' An array of ACE
entries.
> >
> > Dim sizePtr As IntPtr =
> > Marshal.AllocHGlobal(Marshal.SizeOf(GetType(ACL_SI ZE_INFORMATION))) > > Dim AclInformationLength As Integer =
> > Marshal.SizeOf(GetType(ACL_SIZE_INFORMATION))
> > If GetAclInformation(DACL, sizePtr, AclInformationLength, 2) =
0
> > Then
> > MsgBox("error: " & Err.LastDllError)
> > End If
> >
> > Dim size As Integer =
CType(Marshal.PtrToStructure(sizePtr, > > GetType(ACL_SIZE_INFORMATION)), ACL_SIZE_INFORMATION).AclBytesInUse > >
> > Const ACL_REVISION As Integer = 2
> > ACECount = 1
> > Dim pNewACL As IntPtr
> > Dim newsize As Integer = size +
> > Marshal.SizeOf(GetType(ACCESS_ALLOWED_ACE)) -
> > Marshal.SizeOf(GetType(Integer)) + (SID_MAX_SUB_AUTHORITIES *
> > Marshal.SizeOf(GetType(Integer)))
> > pNewACL = Marshal.AllocHGlobal(newsize)
> > bSuccess = 0
> > bSuccess = InitializeAcl(pNewACL, newsize, ACL_REVISION)
> > ' A return code of zero means the call failed; test for this > > ' before continuing.
> > If (bSuccess = 0) Then
> > MsgBox("Error: Unable to Initialize New ACL")
> > Exit Function
> > End If
> >
> > Dim pAce As IntPtr
> > bSuccess = GetAce(DACL, 0, pAce)
> > If (bSuccess = 0) Then
> > MsgBox("Error: " & _
> > "Unable to Get Ace")
> > MsgBox(Err.LastDllError)
> > End If
> >
> > Dim AceStruct As ACCESS_ALLOWED_ACE =
> > CType(Marshal.PtrToStructure(pAce, GetType(ACCESS_ALLOWED_ACE)),
> > ACCESS_ALLOWED_ACE)
> >
> > bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
> > AceStruct.Header.AceSize)
> > If (bSuccess = 0) Then
> > MsgBox("Error: " & _
> > "Unable to Set Ace ")
> > MsgBox(Err.LastDllError)
> > 'Exit Function
> > End If
> >
> > End Function
> >
> >
> > I'm not sure if that's what you are looking for. If you are looking to use
> > the EXPLICIT_ACCESS structure, you might want to look into the

functions
> > that go with it (which I mentioned earlier - they are in the 'See

Also'
> > section in the documentation for the structure).
> >
> > hope that helps..
> > Imran.
> >
> > "Dave Coate" <co*****@comcast.net> wrote in message
> > news:ed**************************@posting.google.c om...
> > > Imran, Thanks for spotting that (doh!) mistake. I knew that.
Just > > > staring at the code too long. However, after fixing it, I am

getting > > > the same errors on the next function in my code, AddAce(). I double > > > checked the documentation this time and I believe the function

should
> > > be defined with all 5 parameters using ByVal. (The first one is
> > > ambiguous, but since ByRef throws an exception I think I'll go with > > > ByVal!)
> > >
> > > Anyway, here are additions to my code (following the GetAce call) and
> > > the relevant Functions, Structures and Enumerations:
> > >
> > > Dave
> > >
> > > *************************************************
> > >
> > > Const ACL_REVISION = 2
> > > ACECount = 1
> > > Dim pNewACL As IntPtr
> > > bSuccess = InitializeAcl(pNewACL, ACECount *
> > > EXPLICIT_ACCESS.SIZEOF_EA1, ACL_REVISION)
> > > ' A return code of zero means the call failed; test for this > > > ' before continuing.
> > > If (bSuccess = 0) Then
> > > MsgBox("Error: Unable to Initialize New ACL")
> > > Exit Function
> > > End If
> > >
> > > bSuccess = AddAce(pNewACL, ACL_REVISION, 0, pAce,
> > > EXPLICIT_ACCESS.SIZEOF_EA1)
> > > If (bSuccess = 0) Then
> > > MsgBox("Error: " & _
> > > "Unable to Set Ace ")
> > > MsgBox(Err.LastDllError)
> > > 'Exit Function
> > > End If
> > >
> > >
> > > Declare Function InitializeAcl Lib "advapi32.dll" ( _
> > > ByRef pAcl As IntPtr, _
> > > ByVal nAclLength As Integer, _
> > > ByVal dwAclRevision As Integer) As Integer
> > >
> > > Declare Function AddAce Lib "advapi32.dll" ( _
> > > ByVal pAcl As IntPtr, _
> > > ByVal dwAceRevision As Integer, _
> > > ByVal dwStartingAceIndex As Integer, _
> > > ByVal pAceList As IntPtr, _
> > > ByVal nAceListLength As Integer) As Integer
> > >
> > > ' This data Structure is used to create the explicit entry

array.
> > > <StructLayout(LayoutKind.Sequential, Pack:=1)> _
> > > Public Structure EXPLICIT_ACCESS
> > > Public Shared ReadOnly SIZEOF_EA1 As Integer
> > > Shared Sub New()
> > > SIZEOF_EA1 =
Marshal.SizeOf(GetType(EXPLICIT_ACCESS)) > > > End Sub
> > > Public grfAccessPermissions As UInt32
> > > Public grfAccessMode As ACCESS_MODE
> > > Public grfInheritance As UInt32
> > > Public Trustee As Trustee
> > > End Structure
> > >
> > > ' This Structure contains the trustee information for the ACE. > > > <StructLayout(LayoutKind.Sequential, Pack:=1)> _
> > > Public Structure TRUSTEE
> > > Public pMultipleTrustee As IntPtr
> > > Public MultipleTrusteeOperation As

MULTIPLE_TRUSTEE_OPERATION
> > > Public TrusteeForm As TRUSTEE_FORM
> > > Public TrusteeType As TRUSTEE_TYPE
> > > Public ptstrName As IntPtr
> > > End Structure
> > >
> > > ' The ACCESS_MODE enumeration tells what type of ACE entry

we're > > > ' working with.
> > > Public Enum ACCESS_MODE
> > > NOT_USED_ACCESS = 0
> > > GRANT_ACCESS
> > > SET_ACCESS
> > > DENY_ACCESS
> > > REVOKE_ACCESS
> > > SET_AUDIT_SUCCESS
> > > SET_AUDIT_FAILURE
> > > End Enum
> > >
> > > ' The MULTIPLE_TRUSTEE_OPERATION enumeration determines if this > > > ' is a single or a multiple trustee.
> > > Public Enum MULTIPLE_TRUSTEE_OPERATION
> > > NO_MULTIPLE_TRUSTEE
> > > TRUSTEE_IS_IMPERSONATE
> > > End Enum
> > >
> > > ' The TRUSTEE_FORM enumeration determines what form the ACE
> > > trustee
> > > ' takes.
> > > Public Enum TRUSTEE_FORM
> > > TRUSTEE_IS_SID
> > > TRUSTEE_IS_NAME
> > > TRUSTEE_BAD_FORM
> > > TRUSTEE_IS_OBJECTS_AND_SID
> > > TRUSTEE_IS_OBJECTS_AND_NAME
> > > End Enum
> > >
> > > ' The TRUSTEE_TYPE enumeration determins the type of the

trustee.
> > > Public Enum TRUSTEE_TYPE
> > > TRUSTEE_IS_UNKNOWN
> > > TRUSTEE_IS_USER
> > > TRUSTEE_IS_GROUP
> > > TRUSTEE_IS_DOMAIN
> > > TRUSTEE_IS_ALIAS
> > > TRUSTEE_IS_WELL_KNOWN_GROUP
> > > TRUSTEE_IS_DELETED
> > > TRUSTEE_IS_INVALID
> > > TRUSTEE_IS_COMPUTER
> > > End Enum
> > >
> > >
> > >
> > > "Imran Koradia" <no****@microsoft.com> wrote in message
> > news:<ed**************@TK2MSFTNGP10.phx.gbl>...
> > > > > Declare Function GetAce Lib "advapi32.dll" ( _
> > > > > ByRef pAcl As IntPtr, _
> > > > > ByVal dwAceIndex As Integer, _
> > > > > ByRef pAce As IntPtr) As Boolean
> > > >
> > > > Change pAcl from 'ByRef' to 'ByVal'.
> > > >
> > > > hope that helps..
> > > > Imran.
> >
> >
>
>



Nov 21 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.