21 October, 2009
Setting the Owner of Files and Directories in C#
I recently had a requirement to set the owners of users home directories on a Windows server. The .Net framework has built-in support for this so it simply looked like a case of doing this:
IdentityReference owner = new NTAccount(“MYDOMAIN\MyUser”); DirectoryInfo directory = new DirectoryInfo(“c:\myDirectory”); DirectorySecurity directorySecurity = directory.GetAccessControl(); directorySecurity.SetOwner(owner); directory.SetAccessControl(directorySecurity);
However, when I tried this I just got an InvalidOperationException with the message “The security identifier is not allowed to be the owner of this object”.
It turns out that the reason for this is that normally you are only allowed to take ownership yourself, and not assign it. For a user to take ownership they must either have been given the Take Ownership permission or be an administrator.
There is one exception to this rule, and that is if the user has the Restore files and directories privilege then they can assign ownership to other users. Now administrators have this privilege, but by default it is disabled. More details on this from Microsoft.
So to set the owner of the file, you need to have the Restore Files and Directories privilege and then enable it before setting the owner. Really we should also disable the privilege when we’ve finished using it as well.
Unfortunately .Net doesn’t have built in support for this yet, so we are reduced to PInvoking native methods and here’s the code:
sealed class UnmanagedCode { [DllImport(“kernel32.dll”, SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool CloseHandle(IntPtr hObject); // Use this signature if you do not want the previous state [DllImport(“advapi32.dll”, SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool AdjustTokenPrivileges(IntPtr tokenHandle, [MarshalAs(UnmanagedType.Bool)]bool disableAllPrivileges, ref TOKEN_PRIVILEGES newState, UInt32 bufferLength, IntPtr previousState, IntPtr returnLength); [DllImport(“kernel32.dll”, ExactSpelling = true)] static extern IntPtr GetCurrentProcess(); [DllImport(“advapi32.dll”, ExactSpelling = true, SetLastError = true)] static extern bool OpenProcessToken (IntPtr processHandle, int desiredAccess, ref IntPtr phtok); [DllImport(“advapi32.dll”, SetLastError = true)] static extern bool LookupPrivilegeValue (string host, string name, ref LUID lpLuid); [StructLayout(LayoutKind.Sequential, Pack = 1)] struct TOKEN_PRIVILEGES { public UInt32 PrivilegeCount; public LUID Luid; public UInt32 Attributes; } [StructLayout(LayoutKind.Sequential)] public struct LUID { public uint LowPart; public int HighPart; } const int SE_PRIVILEGE_ENABLED = 0x00000002; const int TOKEN_QUERY = 0x00000008; const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; //http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx const string SE_RESTORE_PRIVILEGE = “SeRestorePrivilege”; public static void GiveRestorePrivilege() { TOKEN_PRIVILEGES tokenPrivileges; tokenPrivileges.PrivilegeCount = 1; tokenPrivileges.Luid = new LUID(); tokenPrivileges.Attributes = SE_PRIVILEGE_ENABLED; IntPtr tokenHandle = RetrieveProcessToken(); try { bool success = LookupPrivilegeValue (null, SE_RESTORE_PRIVILEGE, ref tokenPrivileges.Luid); if (success == false) { int lastError = Marshal.GetLastWin32Error(); throw new Exception( string.Format(“Could not find privilege {0}. Error {1}”, SE_RESTORE_PRIVILEGE, lastError)); } success = AdjustTokenPrivileges( tokenHandle, false, ref tokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero); if (success == false) { int lastError = Marshal.GetLastWin32Error(); throw new Exception( string.Format(“Could not assign privilege {0}. Error {1}”, SE_RESTORE_PRIVILEGE, lastError)); } } finally { CloseHandle(tokenHandle); } } static IntPtr RetrieveProcessToken() { IntPtr processHandle = GetCurrentProcess(); IntPtr tokenHandle = IntPtr.Zero; bool success = OpenProcessToken(processHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref tokenHandle); if (success == false) { int lastError = Marshal.GetLastWin32Error(); throw new Exception( string.Format(“Could not retrieve process token. Error {0}”, lastError)); } return tokenHandle; } }
To remove the privilege you will just need to replace SE_PRIVILEGE_ENABLED with the disabled value. In my use I’m just setting the owner and finishing the process so it’s not really required.