Note that there are some explanatory texts on larger screens.

plurals
  1. POImpersonation and CurrentUser Registry Access
    primarykey
    data
    text
    <p>Environment: Windows XP SP3, C#, .Net 4.0</p> <p>Problem:</p> <p>I'm attempting to add access to an impersonated users registry hive in an impersonation class and I'm running into issues based on the type of user being impersonated (or more accurately the limitation seems to be on the impersonating user).</p> <p>I was originally following <a href="http://www.codeproject.com/Articles/125810/A-complete-Impersonation-Demo-in-Csharp-NET.aspx" rel="noreferrer">an impersonation example from CodeProject</a> which showed a call to <code>LoadUserProfile()</code> taking place after impersonation was started using a duplicate token generated througha call to <code>DuplcateToken()</code> from the original token obtained from <code>LogonUser()</code>. I was not able to get this example working in my environment impersonating a limited user from an administrator account (From the screen shots included in the example it appears as though it was done on a windows Vista\7 system and no details were given about the account types involved).</p> <p>The call to <code>LoadUserProfile()</code> was throwing an error of "Access Denied". Looking at userenv.log showed the line "LoadUserProfile: failed to enable the restore privilege. error c0000022". The LoadUserProfile documentation on MSDN shows that the calling process must posses the SE_RESTORE_NAME and SE_BACKUP_NAME privileges which by default only members of the Administrators and Backup Operators groups have. <i>(As a side note when I attempted to add these two privileges later on to the Users group i still received Access Denied but the userenv.log showed "DropClientContext: Client [number] does not have enough permission. error 5" which I couldn't find any information on)</i></p> <p>Given that the user I was impersonating did not have these privileges I moved the call to <code>LoadUserProfile()</code> up to before starting the impersonation and this time it loaded without a problem and I was able to read and write to it in this test. Thinking that I had discovered my answer I created a conditional check for the type of account so that <code>LoadUserProfile()</code> would be called before impersonation if the current user was a member of Administrators or wait until after impersonation if the member was not a member of Administrators (in the later instance I would be relying on the impersonated user having these privileges). Unfortunately I was wrong; I had not discovered my answer. When I tested the call with the role reversed (User > Administrator) The call to <code>LoadUserProfile()</code> still failed again with the Access Denied error and userenv.log showed the same "LoadUserProfile: failed to enable the restore privilege. error <b>c0000061</b>" but with a different error number this time.</p> <p>Thinking that the privileges may not be enabled by default on the tokens returned from <code>LogonUser()</code> and\or <code>DuplicateToken()</code> I added two calls to <code>AdjustTokenPrivilege()</code> on the current users token (taking place after impersonation) obtained from <code>WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token</code>. <code>TokenAccessLevels.AdjustPrivileges</code> and <code>TokenAccessLevels.Query</code> were specified because the documentation for AdjustTokenPrivilege on MSDN specifies that they are needed on the token being adjusted <i>(I also tried obtaining the token through a call to <code>OpenProcessToken()</code> using a handle retrieved from <code>System.Diagnostics.Process.GetCurrentProcess().Handle</code> but that failed when called from the User both inside and outside of impersonation with <code>GetCurrentProcess()</code> being the function that threw access denied)</i></p> <p><code>AdjustTokenPrivilege()</code> returned successfully when used with the <code>WindowsIdentity...Token</code> but <code>LoadUserProfile()</code> still resulted in Access Denied (restore privilege). At this point I was not convinced that <code>AdjustTokenPrivilege()</code> was doing it's job so I set out to determine what privileges were available and what state they were in for a particular token with <code>GetTokenInformation()</code> which resulted in it's own little bag of fun. After learning some new things I was able to call <code>GetTokenInformation()</code> and print out a list of privileges and their current status but the results were somewhat inconclusive since both Restore and Backup showed an attribute of 0 before and after calling <code>AdjustTokenPrivilege()</code> both as the administrator and while impersonating the administrator (Strangely three other privileges changed from 2 to 1 on the token when calling <code>AdjustTokenPrivilege()</code> but not the ones actually being adjusted which remained at a value of 0)</p> <p>I removed the call to <code>DuplicateToken()</code> and replaced all of the places it was being used with the token returned from <code>LogonUser()</code> to see if that would help though in testing the privileges on the tokens the <code>LogonUser()</code> and <code>DuplicateToken()</code> tokens were identical. When I initially wrote the impersonation class I had been using the primary token in my call to <code>WindowsImpersonationContext.Impersonate()</code> without any problems and figured it was worth a try.</p> <p>In the code example I've provided below I am able to impersonate and access the registry of a User when run as an Administrator but not the other way around. Any help would be greatly appreciated.</p> <p>Pre-Post Edit:</p> <p>I've also tried using the <code>RegOpenCurrentUser()</code> API in place of <code>LoadUserProfile()</code> and had success with Administrator > self and Administrator > User impersonation but when impersonating an Administrator from either another Administrator account or a User <code>RegOpenCurrentUser()</code> returns a pointer to HKEY_USERS\S-1-5-18 (what ever that is) instead of the actual account hive. I'm guessing because it is not actually loaded which brings me back to square one with needing to use <code>LoadUserProfile()</code></p> <p>From RegOpenCurrentUser documentation (MSDN):</p> <blockquote> <p>RegOpenCurrentUser uses the thread's token to access the appropriate key, or the default if the profile is not loaded.</p> </blockquote> <p>Code Snippet:</p> <pre><code>// Private variables used by class private IntPtr tokenHandle; private PROFILEINFO pInfo; private WindowsImpersonationContext thisUser; private string sDomain = string.Empty; private string sUsername = string.Empty; private string sPassword = string.Empty; private bool bDisposed = false; private RegistryKey rCurrentUser = null; private SafeRegistryHandle safeHandle = null; //Constants used for privilege adjustment private const string SE_RESTORE_NAME = "SeRestorePrivilege"; private const string SE_BACKUP_NAME = "SeBackupPrivilege"; private const UInt32 SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001; private const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002; private const UInt32 SE_PRIVILEGE_REMOVED = 0x00000004; private const UInt32 SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000; [StructLayout(LayoutKind.Sequential)] protected struct PROFILEINFO { public int dwSize; public int dwFlags; [MarshalAs(UnmanagedType.LPTStr)] public String lpUserName; [MarshalAs(UnmanagedType.LPTStr)] public String lpProfilePath; [MarshalAs(UnmanagedType.LPTStr)] public String lpDefaultPath; [MarshalAs(UnmanagedType.LPTStr)] public String lpServerName; [MarshalAs(UnmanagedType.LPTStr)] public String lpPolicyPath; public IntPtr hProfile; } protected struct TOKEN_PRIVILEGES { public UInt32 PrivilegeCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public LUID_AND_ATTRIBUTES[] Privileges; } [StructLayout(LayoutKind.Sequential)] protected struct LUID_AND_ATTRIBUTES { public LUID Luid; public UInt32 Attributes; } [StructLayout(LayoutKind.Sequential)] protected struct LUID { public uint LowPart; public int HighPart; } // Private API calls used by class [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] protected static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)] protected static extern bool LoadUserProfile(IntPtr hToken, ref PROFILEINFO lpProfileInfo); [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)] protected static extern bool UnloadUserProfile(IntPtr hToken, IntPtr hProfile); [DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)] protected static extern bool CloseHandle(IntPtr hObject); [DllImport("advapi32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)] protected static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 Zero, IntPtr Null1, IntPtr Null2); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)][return: MarshalAs(UnmanagedType.Bool)] protected static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid); [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] public void Start() { tokenHandle = IntPtr.Zero; // set the pointer to nothing if (!LogonUser(sUsername, sDomain, sPassword, 2, 0, ref tokenHandle)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } // end if !LogonUser returned false try { //All of this is for loading the registry and is not required for impersonation to start LUID LuidRestore = new LUID(); LUID LuidBackup = new LUID(); if(LookupPrivilegeValue(null, SE_RESTORE_NAME, ref LuidRestore) &amp;&amp; LookupPrivilegeValue(null, SE_BACKUP_NAME, ref LuidBackup)) { //Create the TokenPrivileges array to pass to AdjustTokenPrivileges LUID_AND_ATTRIBUTES[] LuidAndAttributes = new LUID_AND_ATTRIBUTES[2]; LuidAndAttributes[0].Luid = LuidRestore; LuidAndAttributes[0].Attributes = SE_PRIVILEGE_ENABLED; LuidAndAttributes[1].Luid = LuidBackup; LuidAndAttributes[1].Attributes = SE_PRIVILEGE_ENABLED; TOKEN_PRIVILEGES TokenPrivileges = new TOKEN_PRIVILEGES(); TokenPrivileges.PrivilegeCount = 2; TokenPrivileges.Privileges = LuidAndAttributes; IntPtr procHandle = WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token; if(AdjustTokenPrivileges(procHandle, false, ref TokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero)) { pInfo = new PROFILEINFO(); pInfo.dwSize = Marshal.SizeOf(pInfo); pInfo.lpUserName = sUsername; pInfo.dwFlags = 1; LoadUserProfile(tokenHandle, ref pInfo); //this is not required to take place if(pInfo.hProfile != IntPtr.Zero) { safeHandle = new SafeRegistryHandle(pInfo.hProfile, true); rCurrentUser = RegistryKey.FromHandle(safeHandle); }//end if pInfo.hProfile }//end if AdjustTokenPrivileges }//end if LookupPrivilegeValue 1 &amp; 2 }catch{ //We don't really care that this didn't work but we don't want to throw any errors at this point as it would stop impersonation }//end try WindowsIdentity thisId = new WindowsIdentity(tokenHandle); thisUser = thisId.Impersonate(); } // end function Start </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload