Impersonating CurrentUser from SYSTEM
Hey all,
I've been searching for a very long time an easy way to impersonate current user while running in a "SYSTEM" user credentials.
I haven't found any code that does it and actually works.
So I managed to combine pieces of code from several places, and added some of my own.
This helper class exposes 2 functions:
1. Get current user impersonated scope - in this scope you can do pretty much whatever you want in the current user credentials.
2. Start a process as current user.
I really hope this code will save you the hours I spend on writing it.
I've been searching for a very long time an easy way to impersonate current user while running in a "SYSTEM" user credentials.
I haven't found any code that does it and actually works.
So I managed to combine pieces of code from several places, and added some of my own.
This helper class exposes 2 functions:
1. Get current user impersonated scope - in this scope you can do pretty much whatever you want in the current user credentials.
2. Start a process as current user.
public static class ImpersonationUtils { private const int SW_SHOW = 5; private const int TOKEN_QUERY = 0x0008; private const int TOKEN_DUPLICATE = 0x0002; private const int TOKEN_ASSIGN_PRIMARY = 0x0001; private const int STARTF_USESHOWWINDOW = 0x00000001; private const int STARTF_FORCEONFEEDBACK = 0x00000040; private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400; private const int TOKEN_IMPERSONATE = 0x0004; private const int TOKEN_QUERY_SOURCE = 0x0010; private const int TOKEN_ADJUST_PRIVILEGES = 0x0020; private const int TOKEN_ADJUST_GROUPS = 0x0040; private const int TOKEN_ADJUST_DEFAULT = 0x0080; private const int TOKEN_ADJUST_SESSIONID = 0x0100; private const int STANDARD_RIGHTS_REQUIRED = 0x000F0000; private const int TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID; [StructLayout(LayoutKind.Sequential)] private struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [StructLayout(LayoutKind.Sequential)] private struct SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public bool bInheritHandle; } [StructLayout(LayoutKind.Sequential)] private struct STARTUPINFO { public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public int dwX; public int dwY; public int dwXSize; public int dwYSize; public int dwXCountChars; public int dwYCountChars; public int dwFillAttribute; public int dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } private enum SECURITY_IMPERSONATION_LEVEL { SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation } private enum TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation } [DllImport("advapi32.dll", SetLastError = true)] private static extern bool CreateProcessAsUser( IntPtr hToken, string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool DuplicateTokenEx( IntPtr hExistingToken, int dwDesiredAccess, ref SECURITY_ATTRIBUTES lpThreadAttributes, int ImpersonationLevel, int dwTokenType, ref IntPtr phNewToken); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool OpenProcessToken( IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle); [DllImport("userenv.dll", SetLastError = true)] private static extern bool CreateEnvironmentBlock( ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit); [DllImport("userenv.dll", SetLastError = true)] private static extern bool DestroyEnvironmentBlock( IntPtr lpEnvironment); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle( IntPtr hObject); private static void LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock, int sessionId) { var pi = new PROCESS_INFORMATION(); var saProcess = new SECURITY_ATTRIBUTES(); var saThread = new SECURITY_ATTRIBUTES(); saProcess.nLength = Marshal.SizeOf(saProcess); saThread.nLength = Marshal.SizeOf(saThread); var si = new STARTUPINFO(); si.cb = Marshal.SizeOf(si); si.lpDesktop = @"WinSta0\Default"; si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK; si.wShowWindow = SW_SHOW; if (!CreateProcessAsUser( token, null, cmdLine, ref saProcess, ref saThread, false, CREATE_UNICODE_ENVIRONMENT, envBlock, null, ref si, out pi)) { throw new Win32Exception(Marshal.GetLastWin32Error(), "CreateProcessAsUser failed"); } } private static IDisposable Impersonate(IntPtr token) { var identity = new WindowsIdentity(token); return identity.Impersonate(); } private static IntPtr GetPrimaryToken(Process process) { var token = IntPtr.Zero; var primaryToken = IntPtr.Zero; if (OpenProcessToken(process.Handle, TOKEN_DUPLICATE, ref token)) { var sa = new SECURITY_ATTRIBUTES(); sa.nLength = Marshal.SizeOf(sa); if (!DuplicateTokenEx( token, TOKEN_ALL_ACCESS, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary, ref primaryToken)) { throw new Win32Exception(Marshal.GetLastWin32Error(), "DuplicateTokenEx failed"); } CloseHandle(token); } else { throw new Win32Exception(Marshal.GetLastWin32Error(), "OpenProcessToken failed"); } return primaryToken; } private static IntPtr GetEnvironmentBlock(IntPtr token) { var envBlock = IntPtr.Zero; if (!CreateEnvironmentBlock(ref envBlock, token, false)) { throw new Win32Exception(Marshal.GetLastWin32Error(), "CreateEnvironmentBlock failed"); } return envBlock; } public static void LaunchAsCurrentUser(string cmdLine) { var process = Process.GetProcessesByName("explorer").FirstOrDefault(); if (process != null) { var token = GetPrimaryToken(process); if (token != IntPtr.Zero) { var envBlock = GetEnvironmentBlock(token); if (envBlock != IntPtr.Zero) { LaunchProcessAsUser(cmdLine, token, envBlock, process.SessionId); if (!DestroyEnvironmentBlock(envBlock)) { throw new Win32Exception(Marshal.GetLastWin32Error(), "DestroyEnvironmentBlock failed"); } } CloseHandle(token); } } } public static IDisposable ImpersonateCurrentUser() { var process = Process.GetProcessesByName("explorer").FirstOrDefault(); if (process != null) { var token = GetPrimaryToken(process); if (token != IntPtr.Zero) { return Impersonate(token); } } throw new Exception("Could not find explorer.exe"); } }And the usage is very easy:
ImpersonationUtils.LaunchAsCurrentUser("notepad"); using (ImpersonationUtils.ImpersonateCurrentUser()) { }It's easy and it's fun!
I really hope this code will save you the hours I spend on writing it.
You are a genius!!
ReplyDeleteThanks so much! Where can I donate for your efforts ?
I had trouble listing networked drives in a service. Your solution solved this effortlessly.
Hi,
DeleteHow can did you use this? I need to copy files from a network share to a local path using a service. I have tried the using statement but it fails.
Cheers
No problem :).
ReplyDeleteNo need to donate, ur appreciation is enough!
Your version works like a charm plus it is easy as pie :) I modified it though, to return a bool if the process was launched successfully.
ReplyDeleteWhat do not work are :
http://stackoverflow.com/questions/2313553/process-start-with-different-credentials-with-uac-on
http://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-and-bit-Archite
And I replaced '.FirstOrDefault()' with '[0]'
DeleteThanks :)
DeleteIf you really want, you can upvote my response here:
http://stackoverflow.com/questions/3891260/impersonation-the-current-user-using-windowsimpersonationcontext-to-access-netwo
So other people might be able to get it.
notepad???
ReplyDeleteHi, Thank you for writing this, It is very helpful. I want to map a drive under current user but for some reason it is not working. Could you point me in the right direction. I'm using
ReplyDeleteSystem.Diagnostics.Process.Start("net.exe", "use /persistent:NO H: " + path);
any help will be much appreciated.
Try to combine my solution together with:
Deletehttps://stackoverflow.com/questions/3753758/creating-virtual-hard-drive
(The first response)
Other then that, I don't know
Sounds like there may be an issue with delegation. Is there a way to add delegation to the current class? virtual hdd link didn't work :(. Thank you for your reply on this.
ReplyDeleteDo I need to change any local policy for this to work?
ReplyDelete