One of the things you learn when studying for a Microsoft certification exam is that an Administrator can only "take" ownership of a file... that ownership can not be arbitrarily assigned to another account. Although this is true from the Windows user interface perspective, the Windows API has no such restriction.
Note: If you're taking a certification exam, you'd better answer the question from Microsoft's point of view!
BTW: The SubInAcl command-line utility from the Windows Resource Kit can assign the ownership of a file to an arbitrary user.
API Declarations
As you might expect, practically all of the requirements of this example are accomplished via Windows API calls. So, let's start with declaring the API stuff:
Imports System.Runtime.InteropServices
Imports System.IO
Imports System.ComponentModel
Private Const SE_FILE_OBJECT As Integer = 1
Private Const OWNER_SECURITY_INFORMATION As Integer = 1
Private Const NAME_SIZE As Integer = 64
Private Const SID_SIZE As Integer = 32
Declare Auto Function SetNamedSecurityInfo Lib "AdvAPI32.DLL" ( _
ByVal pObjectName As String, _
ByVal ObjectType As Integer, _
ByVal SecurityInfo As Integer, _
ByVal psidOwner As IntPtr, _
ByVal psidGroup As IntPtr, _
ByVal pDacl As IntPtr, _
ByVal pSacl As IntPtr _
) As Integer
Declare Auto Function LookupAccountName Lib "advapi32.dll" ( _
ByVal lpSystemName As String, _
ByVal lpAccountName As String, _
ByVal Sid As IntPtr, _
ByRef cbSid As Integer, _
ByVal lpReferencedDomainName As String, _
ByRef cchReferencedDomainName As Integer, _
ByRef peUse As Integer _
) As Boolean
API Documentation Links
The Example Code
The guts of the program are as follows... We use the LookupAccountName API to take the UserName and turn it into a Security Identifier (SID), we use SetNamedSecurityInfo to write the new owner back to the file.
Dim pNewOwner, deUse As IntPtr
Dim Win32Error As Win32Exception
Dim domain_name As String
Dim ret, sid_len, domain_len As Integer
' Convert the name to a valid SID
sid_len = SID_SIZE
pNewOwner = Marshal.AllocHGlobal(sid_len)
domain_len = NAME_SIZE
domain_name = Space(domain_len)
If LookupAccountName(Nothing, strUserName, pNewOwner, sid_len, domain_name, _
domain_len, deUse) = False Then
ret = Marshal.GetLastWin32Error()
Win32Error = New Win32Exception(ret)
Throw New Exception(Win32Error.Message)
End If
' write the new Owner
ret = SetNamedSecurityInfo(strPath, SE_FILE_OBJECT, _
OWNER_SECURITY_INFORMATION, pNewOwner, Nothing, Nothing, Nothing)
If ret <>> 0 Then
Win32Error = New Win32Exception(ret)
Throw New Exception(Win32Error.Message)
End If
' clean up and go home
Marshal.FreeHGlobal(pNewOwner)
Adding Privileges
Now that you understand how to use API calls to change the ownership of a file, I have a small confession to make... this example doesn't work yet. The problem is that changing the ownership of a file is a tightly-held permission, one that is not enabled by default. To solve this problem we need to enable the permissions for "TakeOwnership" and "Restore".
API Declarations
Again, to accomplish these tasks, we require lots of Windows API calls. So, let's start with the API declarations:
Imports System
Imports System.Diagnostics
Imports System.Runtime.InteropServices
<StructLayout(LayoutKind.Sequential, Pack:=4)> _
Private Structure LUID_AND_ATTRIBUTES
Dim Luid As Long
Dim Attributes As Integer
End Structure
<StructLayout(LayoutKind.Sequential, Pack:=4)> _
Private Structure TOKEN_PRIVILEGES
Dim PrivilegeCount As Integer
Dim Privilege1 As LUID_AND_ATTRIBUTES
Dim Privilege2 As LUID_AND_ATTRIBUTES
End Structure
Private Declare Function OpenProcessToken Lib "advapi32.dll" ( _
ByVal ProcessHandle As IntPtr, _
ByVal DesiredAccess As Integer, _
ByRef TokenHandle As IntPtr _
) As Boolean
Private Declare Auto Function LookupPrivilegeValue Lib "advapi32.dll" ( _
ByVal lpSystemName As String, _
ByVal lpName As String, _
ByRef lpLuid As Long _
) As Boolean
Private Declare Function AdjustTokenPrivileges Lib "advapi32.dll" ( _
ByVal TokenHandle As IntPtr, _
ByVal DisableAllPrivileges As Boolean, _
ByRef NewState As TOKEN_PRIVILEGES, _
ByVal BufferLength As Integer, _
ByVal PreviousState As IntPtr, _
ByVal ReturnLength As IntPtr _
) As Boolean
Private Const TOKEN_QUERY As Integer = &H8
Private Const TOKEN_ADJUST_PRIVILEGES As Integer = &H20
Private Const SE_SECURITY_NAME As String = "SeSecurityPrivilege"
Private Const SE_PRIVILEGE_ENABLED As Integer = &H2
API Documentation Links
The Example Code
The guts of this section is as follows... we use the OpenProcessToken API to get the security "token" of the application, we use LookupPrivilegeValue to get the privileges we need to use, then we use AdjustTokenPrivileges to enable those privileges. Obviously, you can't enable privileges that you don't already have!
Public Function SetPrivileges() As Boolean
Dim hProc, hToken As IntPtr
Dim luid_TakeOwnership, luid_Restore As Long
Dim tp As New TOKEN_PRIVILEGES
' get the current process's token
hProc = Process.GetCurrentProcess().Handle
hToken = IntPtr.Zero
If Not OpenProcessToken(hProc, TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, _
hToken) Then
Return False
End If
' get the LUIDs for the privileges
luid_TakeOwnership = 0
If Not LookupPrivilegeValue(Nothing, SE_TAKE_OWNERSHIP_NAME, _
luid_TakeOwnership) Then
Return False
End If
luid_Restore = 0
If Not LookupPrivilegeValue(Nothing, SE_RESTORE_NAME, luid_Restore) Then
Return False
End If
tp.PrivilegeCount = 2
tp.Privilege1.Luid = luid_TakeOwnership
tp.Privilege1.Attributes = SE_PRIVILEGE_ENABLED
tp.Privilege2.Luid = luid_Restore
tp.Privilege2.Attributes = SE_PRIVILEGE_ENABLED
' enable the privileges
If Not AdjustTokenPrivileges(hToken, False, tp, 0, IntPtr.Zero, IntPtr.Zero) Then
Return False
End If
Return True
End Function
Downloads/Links
Read a related article on Getting the Owner of a File
Download the VB.Net Source code example used in this article:
ChangeOwner.zip