How To Use Visual Basic to Programmatically Change Ownership of a File or Folder


Summary


This article explains how to call the low-level access control functions from Microsoft Visual Basic to change ownership of a file or folder.

More Information


If a user has been granted the SE_RESTORE_NAME privilege ("Restore files and directories"), that user can change the owner of a file or folder on a secure file system. By default, Administrators and Backup Operators hold the SE_RESTORE_NAME privilege.

In addition to being present in the process access token, the SE_RESTORE_NAME privilege must also be granted (enabled). You can grant this privilege by using the AdjustTokenPrivileges() function. After the privilege has been granted, the process can set any valid user or group SID as the owner of a file or folder.

Sample Code

The following sample Visual Basic code demonstrates how to grant the SE_RESTORE_NAME privilege and then set the owner of a file or directory. The ChangeOwnerOfFile() function takes a file or folder name and a user name. It then makes the specified user the owner of the specified file or folder.

NOTE: This code is useful only when targeting files or folders that reside on a secure file system, such as NTFS. The FAT and FAT32 file systems are not secure, and therefore, files on these file systems do not have owners.

Option Explicit

' Global constants we must use with security descriptor
Private Const SECURITY_DESCRIPTOR_REVISION = 1
Private Const OWNER_SECURITY_INFORMATION = 1&

' Access Token constants
Private Const TOKEN_ASSIGN_PRIMARY = &H1
Private Const TOKEN_DUPLICATE = &H2
Private Const TOKEN_IMPERSONATE = &H4
Private Const TOKEN_QUERY = &H8
Private Const TOKEN_QUERY_SOURCE = &H10
Private Const TOKEN_ADJUST_PRIVILEGES = &H20
Private Const TOKEN_ADJUST_GROUPS = &H40
Private Const TOKEN_ADJUST_DEFAULT = &H80
Private Const TOKEN_ALL_ACCESS = TOKEN_ASSIGN_PRIMARY _
+ TOKEN_DUPLICATE + TOKEN_IMPERSONATE + TOKEN_QUERY _
+ TOKEN_QUERY_SOURCE + TOKEN_ADJUST_PRIVILEGES _
+ TOKEN_ADJUST_GROUPS + TOKEN_ADJUST_DEFAULT
Private Const ANYSIZE_ARRAY = 1

' Token Privileges constants
Private Const SE_RESTORE_NAME = "SeRestorePrivilege"
Private Const SE_PRIVILEGE_ENABLED = 2&

' ACL structure
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 Long
Owner As Long
Group As Long
Sacl As ACL
Dacl As ACL
End Type

' Token structures
Private Type LARGE_INTEGER
lowpart As Long
highpart As Long
End Type

Private Type LUID
lowpart As Long
highpart As Long
End Type

Private Type LUID_AND_ATTRIBUTES
pLuid As LUID
Attributes As Long
End Type

Private Type TOKEN_PRIVILEGES
PrivilegeCount As Long
Privileges(ANYSIZE_ARRAY) As LUID_AND_ATTRIBUTES
End Type

' Win32 API calls
Private Declare Function LookupAccountName Lib "advapi32.dll" _
Alias "LookupAccountNameA" (ByVal lpSystemName As String, _
ByVal lpAccountName As String, Sid As Byte, cbSid As Long, _
ByVal ReferencedDomainName As String, _
cbReferencedDomainName As Long, peUse As Integer) As Long

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

Private Declare Function SetSecurityDescriptorOwner _
Lib "advapi32.dll" (pSecurityDescriptor As SECURITY_DESCRIPTOR, _
pOwner As Any, ByVal bOwnerDefaulted As Long) As Long

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

Private Declare Function OpenProcessToken Lib "advapi32.dll" _
(ByVal ProcessHandle As Long, ByVal DesiredAccess As Long, _
TokenHandle As Long) As Long

Private Declare Function GetCurrentProcess Lib "kernel32" () As Long

Private Declare Function LookupPrivilegeValue Lib "advapi32.dll" _
Alias "LookupPrivilegeValueA" (ByVal lpSystemName As String, _
ByVal lpName As String, lpLuid As LUID) As Long

Private Declare Function AdjustTokenPrivileges Lib "advapi32.dll" _
(ByVal TokenHandle As Long, ByVal DisableAllPrivileges As Long, _
NewState As TOKEN_PRIVILEGES, ByVal BufferLength As Long, _
ByVal PreviousState As Long, ByVal ReturnLength As Long) As Long

Private Declare Function CloseHandle Lib "kernel32" _
(ByVal hObject As Long) As Long

Public Sub ChangeOwnerOfFile(FileName As String, _
OwnerAccountName As String)

' variables for the LookupAccountName API Call
Dim Sid(255) As Byte ' Buffer for the SID
Dim nBufferSize As Long ' Length of SID Buffer
Dim szDomainName As String * 255 ' Domain Name Buffer
Dim nDomain As Long ' Length of Domain Name buffer
Dim peUse As Integer ' SID type
Dim Result As Long ' Return value of Win32 API call

' variables for the InitializeSecurityDescriptor API Call
Dim SecDesc As SECURITY_DESCRIPTOR
Dim Revision As Long

Enable_Privilege (SE_RESTORE_NAME)

nBufferSize = 255
nDomain = 255

Result = LookupAccountName(vbNullString, OwnerAccountName, _
Sid(0), nBufferSize, szDomainName, nDomain, peUse)
If (Result = 0) Then
MsgBox "LookupAccountName failed with error code " _
& Err.LastDllError
Exit Sub
End If

Result = InitializeSecurityDescriptor(SecDesc, _
SECURITY_DESCRIPTOR_REVISION)
If (Result = 0) Then
MsgBox "InitializeSecurityDescriptor failed with error code " _
& Err.LastDllError
Exit Sub
End If

Result = SetSecurityDescriptorOwner(SecDesc, Sid(0), 0)
If (Result = 0) Then
MsgBox "SetSecurityDescriptorOwner failed with error code " _
& Err.LastDllError
Exit Sub
End If

Result = SetFileSecurity(FileName, OWNER_SECURITY_INFORMATION, _
SecDesc)
If (Result = 0) Then
MsgBox "SetFileSecurity failed with error code " _
& Err.LastDllError
Exit Sub
Else
MsgBox "Owner of " & FileName & " changed to " _
& OwnerAccountName
End If

Disable_Privilege (SE_RESTORE_NAME)

End Sub

Public Function Enable_Privilege(Privilege As String) As Boolean
Enable_Privilege = ModifyState(Privilege, True)
End Function

Public Function Disable_Privilege(Privilege As String) As Boolean
Disable_Privilege = ModifyState(Privilege, False)
End Function

Public Function ModifyState(Privilege As String, _
Enable As Boolean) As Boolean

Dim MyPrives As TOKEN_PRIVILEGES
Dim PrivilegeId As LUID
Dim ptrPriv As Long ' Pointer to Privileges Structure
Dim hToken As Long ' Token Handle
Dim Result As Long ' Return Value

Result = OpenProcessToken(GetCurrentProcess(), _
TOKEN_ADJUST_PRIVILEGES, hToken)
If (Result = 0) Then
ModifyState = False
MsgBox "OpenProcessToken failed with error code " _
& Err.LastDllError
Exit Function
End If

Result = LookupPrivilegeValue(vbNullString, Privilege, PrivilegeId)
If (Result = 0) Then
ModifyState = False
MsgBox "LookupPrivilegeValue failed with error code " _
& Err.LastDllError
Exit Function
End If

MyPrives.Privileges(0).pLuid = PrivilegeId
MyPrives.PrivilegeCount = 1
If (Enable) Then
MyPrives.Privileges(0).Attributes = SE_PRIVILEGE_ENABLED
Else
MyPrives.Privileges(0).Attributes = 0
End If

Result = AdjustTokenPrivileges(hToken, False, MyPrives, 0, 0, 0)
If (Result = 0 Or Err.LastDllError <> 0) Then
ModifyState = False
MsgBox "AdjustTokenPrivileges failed with error code " _
& Err.LastDllError
Exit Function
End If

CloseHandle hToken

ModifyState = True

End Function

References


For additional information, click the article number below to view the article in the Microsoft Knowledge Base:

245153 INFO: Changing Ownership of a Securable Object