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