Changing Ownership of a File

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 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

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)> _
    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

    ' 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


Read a related article on Getting the Owner of a File
Download the VB.Net Source code example used in this article: