Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Programmatically set NTFS Permissions

Status
Not open for further replies.

neilkonitzer

Programmer
Jun 27, 2001
168
US
'm hoping to save some time by picking the brains of the Tek-Tips experts. I have an application that programmatically creates a directory structure. Currently, I am using the FileSystemObject to do this. The problem is that the FSO appears to not allow me to programmatically set NTFS permissions to these folders. I need to allow a couple users Full Control of these folders and everyone else receives read-only permission. Can this be done programmatically? I realize that there are security issues at stake here; however, this is a requirement that cannot be changed. Thanks in advance!

Neil Konitzer
Freisoft
 
Paul,
Thanks for the response. However,

"The Knowledge Base(KB) article you requested is currently not available."

Neil Konitzer
Freisoft
 
I rechecked the number and it was correct. I'll attempt to paste it from my MSDN CD. Didn't want to do this; it's a bigun!

HOWTO: Set Security on a NTFS Folder Programmatically

--------------------------------------------------------------------------------
The information in this article applies to:

Microsoft Visual Basic Learning, Professional, and Enterprise Editions for Windows, versions 5.0, 6.0
on the following platforms: NT

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


SUMMARY
This article describes how to set security on a folder using security APIs from Visual Basic. The folder needs to be created on an NTFS partition and you need to be a member of the Administrators group. You also need to have read/write permission (READ_CONTRIOL and WRITE_DAC).



MORE INFORMATION
All objects in Microsoft Windows NT have security attributes that are described by a Security Descriptor. The Security Descriptor contains information about who owns the object and who has access to the object. The Security Descriptor contains an Access Control List (ACL) specifying the permissions for users and groups on the object. There are two types of ACLs: discretionary and system. The discretionary ACL (DACL) is controlled by the owner of the object. The DACL contains an entry for each user, global group, or local group given access permission to the object. Each of these entries in the list has an Access Control Entry (ACE). An ACE contains an ACE_HEADER structure, along with the access permission for that ACE type and the Security Identifier (SID). The ACE_HEADER defines the type of ACE (ACCESS_ALLOWED_ACE_TYPE or ACCESS_DENIED_ACE_TYPE), the size of the ACE, and the control flags for the ACE. The access permission determine the type of permission (that is, read, write, and so on) that the user or group has. The process below describes how to modify the DACL for a directory. This requires adding two ACEs. One ACE for the directory itself and any subdirectories and another ACE for any files in the directory.


Note
The following code changes permissions on a folder to Add & Read or Change.


The folder needs to be created on an NTFS partition.


You need to be an Administrator on the machine in question and have read/write (READ_CONTROL and WRITE_DAC) access to the file or directory.


Step to Reproduce Behavior
Create a Standard EXE project in Visual Basic. Form1 is created by default.


Add a Textbox (Text1) and two CommandButtons (Command1 and Command2) to Form1.


Add the following code to the General Declarations of Form1.



Option Explicit

Private Type SID_IDENTIFIER_AUTHORITY
Value(5) As Byte '6 bytes
End Type

Private Type ACE_HEADER
AceType As Byte
AceFlags As Byte
AceSize As Integer
End Type

Private Type ACCESS_ALLOWED_ACE
Header As ACE_HEADER
Mask As Long
SidStart As Long
End Type

Private Type ACL
AclRevision As Byte
Sbz1 As Byte
ACLSize As Integer
AceCount As Integer
Sbz2 As Integer
End Type

Private Type SECURITY_DESCRIPTOR
Revision As Byte
Sbz1 As Byte
Control As Integer
Owner As Long
Group As Long
sacl As ACL
dacl As ACL
End Type

Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type

Private Declare Function InitializeSecurityDescriptor Lib "advapi32.dll" _
(ByVal pSecurityDescriptor As Long, ByVal dwRevision As Long) As Long

Private Declare Function AllocateAndInitializeSid Lib "advapi32.dll" ( _
pIdentifierAuthority As SID_IDENTIFIER_AUTHORITY, _
ByVal nSubAuthorityCount As Byte, _
ByVal nSubAuthority0 As Long, _
ByVal nSubAuthority1 As Long, _
ByVal nSubAuthority2 As Long, _
ByVal nSubAuthority3 As Long, _
ByVal nSubAuthority4 As Long, _
ByVal nSubAuthority5 As Long, _
ByVal nSubAuthority6 As Long, _
ByVal nSubAuthority7 As Long, _
pSid As Long) _
As Long ' pSid above in AllocateAndInitializeSid: pass pointer byref
' pSid in GetLengthSid below is dereferenced pass byval
Private Declare Function GetLengthSid Lib "advapi32.dll" _
(ByVal pSid As Long) As Long
' pSid is dereferenced in FreeSid pass byval
Private Declare Sub FreeSid Lib "advapi32.dll" (ByVal pSid As Long)
' pSid is dereferenced CopySid pass byval
Private Declare Function CopySid Lib "advapi32.dll" _
(ByVal nDestinationSidLength As Long, _
pDestinationSid As Byte, ByVal pSid As Long) As Long
Private Declare Function InitializeAcl Lib "advapi32.dll" (pacl As Byte, _
ByVal nAclLength As Long, ByVal dwAclRevision As Long) As Long
Private Declare Function SetSecurityDescriptorDacl Lib "advapi32.dll" _
(ByVal pSecurityDescriptor As Long, ByVal bDaclPresent As Long, _
pDacl As Byte, ByVal bDaclDefaulted As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function AddAce Lib "advapi32.dll" (pacl As Byte, _
ByVal dwAceRevision As Long, ByVal dwStartingAceIndex As Long, _
pAceList As Byte, ByVal nAceListLength As Long) As Long
Private Declare Function SetFileSecurity Lib "advapi32.dll" _
Alias "SetFileSecurityA" (ByVal lpFileName As String, _
ByVal SecurityInformation As Long, ByVal pSecurityDescriptor As Long) _
As Long

Private Const SECURITY_DESCRIPTOR_REVISION = (1)
Private Const ACL_REVISION = 2
Private Const GENERIC_READ = &H80000000 ' Read only
Private Const GENERIC_ALL = &H10000000 ' Full Control
Private Const SECURITY_BUILTIN_DOMAIN_RID = &H20
Private Const DOMAIN_ALIAS_RID_ADMINS = &H220
Private Const INHERIT_ONLY_ACE = &H8
Private Const OBJECT_INHERIT_ACE = &H1
Private Const GENERIC_EXECUTE = &H20000000
Private Const MAXDWORD = &HFFFFFFFF
Private Const CONTAINER_INHERIT_ACE = &H2
Private Const DACL_SECURITY_INFORMATION = &H4&
Private Const ACCESS_ALLOWED_ACE_TYPE = &H0&
Private Const READ_CONTROL = &H20000
Private Const SYNCHRONIZE = &H100000
Private Const DELETE = &H10000
Private Const GENERIC_WRITE = &H40000000

Private Function ChangePermission(sFName As String, FirstAceMask As Long, SecondAceMask As Long) As Boolean

' This function will set permissions for Folder specified in sFName
' FirstAceMask will set the mask for the files in the folder
' SecondAceMask will set the mask for the folder and subfolders.

Dim udtSidIdentifierAuthority As SID_IDENTIFIER_AUTHORITY
Dim udtAccessAllowedAce As ACCESS_ALLOWED_ACE
Dim pSid As Long, ACLSize As Long
Dim lAceSize As Long
Dim x As Long
Dim i As Integer

' To assign permissions you need to be a member of the
' Administrators group and the folder must be on an NTFS partition.
For i = 0 To 4
udtSidIdentifierAuthority.Value(i) = 0
Next i
udtSidIdentifierAuthority.Value(5) = 5 ' SECURITY_NT_AUTHORITY

Dim psdl As Long ' used to get security descriptor pointer

' Initialize Security Descriptor - the first paramter is address of the
' security descriptor.
x = InitializeSecurityDescriptor(VarPtr(psdl), _
SECURITY_DESCRIPTOR_REVISION)
If x = 0 Then
MsgBox "InitializeSecurityDescriptor failed"
Exit Function
End If

' Allocate and initialize a Sid; Administrative group
' The first parameter is the Sid Authority Identifier which identifies
' the top level authority which is the administrators group.
' The second parameter indicates that there are 2 subauthorities to
' be placed in the SID.
' The two subauthorities SECURITY_BUILTIN_DOMAIN_RID and
' DOMAIN_ALIAS_RID_ADMINS are RID identifiers for the
' Administrators group.
' The RID is the Relative Identifier found in the SID that identifies
' the user or group. In this case it identifies the Administrators
' group.
' The combinded SID and RIDs identify the Administrators group.
' The last paramter, pSid is a pointer to a pointer to the sid.
' It is passed byref here and later dereferenced passing byval.
' It is passed byval in GetLengthSid, CopySid and FreeSid.
x = AllocateAndInitializeSid(udtSidIdentifierAuthority, 2, _
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, _
0, 0, 0, 0, 0, 0, pSid)
If x = 0 Then
MsgBox "AllocateAndInitializeSid failed"
Exit Function
End If

' Calculate length of ACL
Dim AnAcl As ACL, AnAAA As ACCESS_ALLOWED_ACE

ACLSize = Len(AnAcl) + Len(AnAAA) - Len(x) + GetLengthSid(pSid) + _
Len(AnAAA) - Len(x) + GetLengthSid(pSid)

' Allocate memory for ACL
ReDim bufACL(ACLSize - 1) As Byte

' Init the ACL
' Creates a new ACL structure. This is a variable length structure.
' A byte array is created to hold the contiguous bytes. The size of
' this buffer was calculated above in ACLSize. In this call the pointer
' to the first element of the array is passed.
' The third parameter must be ACL_REVISION.
x = InitializeAcl(bufACL(0), ACLSize, ACL_REVISION)
If x = 0 Then
MsgBox "InitializeAcl failure"
Exit Function
End If

' Set values in AllowedAccessAce structure:
' It is easier to assign values using UDT notation.
' There are 2 types of ACEs. Access allowed and Access denied.
' This field indicates that we are using an Access allowed ACE.
udtAccessAllowedAce.Header.AceType = ACCESS_ALLOWED_ACE_TYPE
' AceFlags field specifies control flags.
' INHERIT_ONLY_ACE does not apply to containers (folder) but to objects
' in the container (files).
' OBJECT_INHERIT_ACE indicates that the ACE is inherited by
' non container objects such as files within the container object
' to which the ACE is assigned.
udtAccessAllowedAce.Header.AceFlags = _
INHERIT_ONLY_ACE Or OBJECT_INHERIT_ACE
' Size of the ACE
lAceSize = Len(AnAAA) - Len(x) + GetLengthSid(pSid)
udtAccessAllowedAce.Header.AceSize = lAceSize
' The Mask specifies access rights granted to the ACE.
' See SDK documentation under ACCESS_MASK for the break down by bits.
udtAccessAllowedAce.Mask = FirstAceMask
ReDim bufAce(lAceSize - 1) As Byte
' Copy the AccessAllowedAce structure(UDT) to buffer.
CopyMemory bufAce(0), udtAccessAllowedAce, lAceSize
' Copy sid to buffer where bufAce is the destination buffer -
' pSid is ptr to source SID.
' y(8) corresponds to SidStart field in AccessAllowedAce struct.
x = CopySid(GetLengthSid(pSid), bufAce(8), pSid)
If x = 0 Then
MsgBox "CopySid fail " & Err.LastDllError
Exit Function
End If

' Add an ACE to ACL. This is done twice.
' This first AddAce call applies to files in the folder.
' The first parameter is a pointer to the variable length ACL which is
' passed using a pointer to the first element in a byte array.
' The second parameter needs to be ACL_REVISION.
' The third paramter specifies the position of the ACE in the
' ACL which in this case is at the end.
' The fourth parameter is a pointer to one or more ACEs.
' These ACEs would be placed in contiguous memory and are
' placed in a byte array the size
' which is placed in the last parameter.
x = AddAce(bufACL(0), ACL_REVISION, MAXDWORD, bufAce(0), _
udtAccessAllowedAce.Header.AceSize)
If x = 0 Then
MsgBox "First AddAce failed " & Err.LastDllError
End If

CopyMemory udtAccessAllowedAce, bufAce(0), _
udtAccessAllowedAce.Header.AceSize

udtAccessAllowedAce.Mask = SecondAceMask
udtAccessAllowedAce.Header.AceFlags = CONTAINER_INHERIT_ACE

CopyMemory bufAce(0), udtAccessAllowedAce, _
udtAccessAllowedAce.Header.AceSize


' bufACL(0) - ptr to first element in byte array that
' contains contents of the acl structure - ACE gets added to this ACL
' which contains ACEs stored contiguously.
' This ACE applies to directories and subdirectories.

x = AddAce(bufACL(0), ACL_REVISION, MAXDWORD, bufAce(0), _
udtAccessAllowedAce.Header.AceSize)
If x = 0 Then
MsgBox "Second AddAce failed " & Err.LastDllError
End If

' Set the DACL in the security descriptor
' The first paramter is the pointer to the security descriptor.
' The second paramter is boolean indicating the presence of a DACL
' in the Security Descriptor.
' The third parameter is the address of the DACL which is variable
' length and passed in a byte array.
' The fourth paramter indicates that DACL is created by user.
x = SetSecurityDescriptorDacl(VarPtr(psdl), 1, bufACL(0), 0)
If x = 0 Then
MsgBox "SetSecurityDescriptorDacl failure " & Err.LastDllError
Exit Function
End If

Dim si As Long
si = DACL_SECURITY_INFORMATION

' Set security on Folder object
x = SetFileSecurity(sFName, si, VarPtr(psdl))
If x = 0 Then
MsgBox "SetFileSecurity failure " & Err.LastDllError
Exit Function
End If

' Free Sid
FreeSid pSid

ChangePermission = True

End Function

Private Sub Command1_Click()

Dim rslt As Boolean
Dim FirstAddReadMask As Long, SecondAddReadMask As Long
' Add & Read
FirstAddReadMask = GENERIC_EXECUTE Or GENERIC_READ
SecondAddReadMask = READ_CONTROL Or SYNCHRONIZE Or &H1BF
' (&h01bf standard rights for folder)
rslt = ChangePermission(Text1.Text, FirstAddReadMask, _
SecondAddReadMask)
If Not rslt Then
MsgBox "ChangePermission failed"
End If

End Sub

Private Sub Command2_Click()

Dim rslt As Boolean, FirstChangeMask As Long, SecondChangeMask As Long
' Change
FirstChangeMask = GENERIC_READ Or GENERIC_EXECUTE Or _
DELETE Or GENERIC_WRITE
SecondChangeMask = READ_CONTROL Or DELETE Or SYNCHRONIZE Or &H1BF
' (&h01bf standard rights for folder)
rslt = ChangePermission(Text1.Text, FirstChangeMask, SecondChangeMask)
If Not rslt Then
MsgBox "ChangePermission failed"
End If

End Sub

Private Sub Form_Load()
Form1.Caption = "Enter folder - click btn to change permissions"
Command1.Caption = "Add && Read permissions"
Command2.Caption = "Change permissions"
Text1.Text = "d:\test"
End Sub

Run the application.


In the TextBox, enter the name of the folder you want to change permissions on. (D:\test is entered by default.)


Click the Add & Read permissions button to give Add & Read permissions to the folder or click the Change Permissions button to give Change permssions to the folder.


To check the permissions on the folder, right-click Explorer. Select the Properties menu item and click on the Security Tab of the Properties dialog. On the Security tab, click the Permissions button. The specific account should say Add & Read or Change depending on which button you clicked in the above sample.


Again, the folder needs to be created on an NTFS partition and you need to be an Administrator on the machine in question and have read/write (READ_CONTROL and WRITE_DAC) access to the file or directory.



REFERENCES
For additional information on using NT Security through code, click the article numbers below to view the articles in the Microsoft Knowledge Base:

Q194757 HOWTO: Add an Access-Allowed ACE to a File Through Visual Basic
Q115948 Creating Access Control Lists for Directories
Q202179 HOWTO: Call Windows API Functions with Special Requirements from Visual Basic

Additional query words:

Keywords : kbAPI kbNTOS400 kbSDKWin32 kbSecurity kbVBp kbVBp500 kbVBp600 kbGrpVB kbDSupport
Version : WINDOWS:5.0,6.0
Platform : WINDOWS
Issue type : kbhowto
Technology : kbvcSearch


Last Reviewed: July 24, 2000
© 2000 Microsoft Corporation. All rights reserved. Terms of Use.




--------------------------------------------------------------------------------
Send feedback to MSDN.Look here for MSDN Online resources.

 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top