Access the HKCU registry hive for an impersonated user in C# - c#

I need to implement functionality that impersonates a domain user. The impersonated thread needs to be able to read from/write to HKCU registry hive for the impersonated user. I am able to impersonate the user, but when I attempt to load any registry keys, I receive a Win32 "Access is denied" exception.
NOTE: The intention here is to provide a pseudo impersonated command-line to perform a specific set of actions as a service account. The service account may not have interactive logon rights, so I am required to use the BATCH logon type. As a test, I did also try the INTERACTIVE logon type, but the result was the same.
I followed this CodeProject article as a general guide. Here is what I have:
partial class Program
{
[DllImport("advapi32.dll")]
public static extern int LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenCurrentUser(int samDesired, out IntPtr phkResult);
[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool LoadUserProfile(IntPtr hToken, ref ProfileInfo lpProfileInfo);
[DllImport("Userenv.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool UnloadUserProfile(IntPtr hToken, IntPtr lpProfileInfo);
[StructLayout(LayoutKind.Sequential)]
public struct ProfileInfo
{
public int dwSize;
public int dwFlags;
public string lpUserName;
public string lpProfilePath;
public string lpDefaultPath;
public string lpServerName;
public string lpPolicyPath;
public IntPtr hProfile;
}
private static string ImpUser = string.Empty;
private static string ImpDomain = string.Empty;
private static string FullyQualifiedImpUser
{
get
{
return $"{ImpDomain}\\{ImpUser}";
}
}
private static SecureString ImpSecret = new SecureString();
private static bool CurrentlyImpersonating = false;
private static WindowsIdentity ImpersonatedIdentity = null;
private static IntPtr Token = IntPtr.Zero;
private static IntPtr TokenDuplicate = IntPtr.Zero;
//*** THIS IS THE CORE METHOD ***
private static void EnterModeImpersonated()
{
bool loadSuccess;
int errCode;
try
{
if (RevertToSelf())
{
if (LogonUser(ImpUser, ImpDomain,
ImpSecret.Plaintext(), Constants.LOGON32_LOGON_TYPE_BATCH,
Constants.LOGON32_PROVIDER_DEFAULT, ref Token) != 0)
{
if (DuplicateToken(Token, Constants.SecurityImpersonation, ref TokenDuplicate) != 0)
{
ImpersonatedIdentity = new WindowsIdentity(TokenDuplicate);
using (WindowsImpersonationContext m_ImpersonationContext = ImpersonatedIdentity.Impersonate())
{
if (m_ImpersonationContext != null)
{
#region LoadUserProfile
// Load user profile
ProfileInfo profileInfo = new ProfileInfo();
profileInfo.dwSize = Marshal.SizeOf(profileInfo);
profileInfo.lpUserName = ImpUser;
profileInfo.dwFlags = 1;
//Here is where I die:
loadSuccess = LoadUserProfile(TokenDuplicate, ref profileInfo);
if (!loadSuccess)
{
errCode = Marshal.GetLastWin32Error();
Win32Exception ex = new Win32Exception(errCode);
throw new Exception($"Failed to load profile for {FullyQualifiedImpUser}. Error code: {errCode}", ex);
}
if (profileInfo.hProfile == IntPtr.Zero)
{
errCode = Marshal.GetLastWin32Error();
Win32Exception ex = new Win32Exception(errCode);
throw new Exception($"Failed accessing HKCU registry for {FullyQualifiedImpUser}. Error code: {errCode}", ex);
}
#endregion
CloseHandle(Token);
CloseHandle(TokenDuplicate);
RegistryAgent.GetRootKeys(profileInfo.hProfile);
EnterMode();
UnloadUserProfile(TokenDuplicate, profileInfo.hProfile);
m_ImpersonationContext.Undo();
RegistryAgent.GetRootKeys(Constants.RevertToInvoker);
}
}
}
else
{
Console.WriteLine("DuplicateToken() failed with error code: " +
Marshal.GetLastWin32Error());
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
catch (Win32Exception we)
{
throw we;
}
catch
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
if (Token != IntPtr.Zero) CloseHandle(Token);
if (TokenDuplicate != IntPtr.Zero) CloseHandle(TokenDuplicate);
Console.WriteLine("After finished impersonation: " +
WindowsIdentity.GetCurrent().Name);
}
}
//Toggles on impersonation mode
//Here, we grab the username, domain and password.
private static bool EnableImpersonation(string userInfo)
{
if (userInfo.Contains('\\'))
{
string[] parts = Parameter.ImpUser.TextValue.Split('\\');
ImpUser = parts[1];
ImpDomain = parts[0];
}
else
{
ImpUser = userInfo;
ImpDomain = Environment.UserDomainName;
}
//Prompt for the invoker to enter the impersonated account password
GetSecret();
if (TryImpersonate())
{
CurrentlyImpersonating = true;
}
else
{
DisableImpersonation();
}
return CurrentlyImpersonating;
}
//Toggles off impersontation & cleans up
private static void DisableImpersonation()
{
ImpSecret = null;
ImpersonatedIdentity = null;
Token = IntPtr.Zero;
TokenDuplicate = IntPtr.Zero;
ImpUser = string.Empty;
ImpDomain = string.Empty;
CurrentlyImpersonating = false;
}
//Implements a console prompt to grab the impersonated account password
//as a SecureString object
private static void GetSecret()
{
ImpSecret = new SecureString();
ConsoleKeyInfo key;
Console.Write($"\r\nEnter the password for {FullyQualifiedImpUser}: ");
do
{
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
{
ImpSecret.AppendChar(key.KeyChar);
Console.Write("*");
}
else
{
if (key.Key == ConsoleKey.Backspace && ImpSecret.Length != 0)
{
ImpSecret.RemoveAt(ImpSecret.Length - 1);
Console.Write("\b \b");
}
}
}
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
}
//This method is intended to ensure that the credentials entered
//for the impersonated user are correct.
private static bool TryImpersonate()
{
IntPtr testToken = IntPtr.Zero;
int result;
try
{
result = LogonUser(ImpUser, ImpDomain, ImpSecret.Plaintext(), Constants.LOGON32_LOGON_TYPE_BATCH, Constants.LOGON32_PROVIDER_DEFAULT, ref testToken);
if (result == 0)
{
int errCode = Marshal.GetLastWin32Error();
Win32Exception ex = new Win32Exception(errCode);
throw new Exception($"Failed to impersonate {FullyQualifiedImpUser}. Error code: {errCode}", ex);
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
}
}
I also read The MSDN documentation for LoadUserProfileA (I didn't find an article for LoadUserProfile() so I have to assume this is the ultimate COM function being called). It indicates:
The token must have TOKEN_QUERY, TOKEN_IMPERSONATE, and TOKEN_DUPLICATE access.. I'm wondering if the logon token or duplicated token needs to be created differently in order to include these rights? I wasn't able to find any documentation on how to manipulate the token rights, though...

I was able to resolve this problem. Here's what I did:
First, there are several Win32 methods which need to be exposed:
[DllImport("advapi32.dll")]
public static extern int LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("userenv.dll")]
public static extern bool LoadUserProfile(IntPtr hToken, ref ProfileInfo lpProfileInfo);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegDisablePredefinedCache();
You'll also need to define a struct in support of calling LoadUserProfile()
[StructLayout(LayoutKind.Sequential)]
public struct ProfileInfo
{
public int dwSize;
public int dwFlags;
public string lpUserName;
public string lpProfilePath;
public string lpDefaultPath;
public string lpServerName;
public string lpPolicyPath;
public IntPtr hProfile;
}
We're going to store the impersonation account password in a SecureString object, but we also want to be able to easily access it as plaintext.
I used the following method to populate the SecureString password, masked, at a console prompt:
public static SecureString GetPasswordAsSecureString(string prompt)
{
SecureString pwd = new SecureString();
ConsoleKeyInfo key;
Console.Write(prompt + #": ");
do
{
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
{
pwd.AppendChar(key.KeyChar);
Console.Write("*");
}
else
{
if (key.Key == ConsoleKey.Backspace && pwd.Length != 0)
{
pwd.RemoveAt(pwd.Length - 1);
Console.Write("\b \b");
}
}
}
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
return pwd;
}
var impPassword = GetPasswordAsSecureString($"Enter the password for {impUser}");
I also recommend defining the following extension method in order to conveniently convert a SecureString to a normal string since one of the Win32 methods we need to use will only accept a normal string:
public static string ToUnSecureString(this SecureString securePassword)
{
if (securePassword == null)
{
return string.Empty;
}
IntPtr unmanagedString = IntPtr.Zero;
try
{
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
return Marshal.PtrToStringUni(unmanagedString);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
}
}
Before doing anything else having to do with impersonation, we need to call the Win32 method RegDisablePredefinedCache(). In terms of our purpose, this method tells Windows to dynamically determine where to look for the HKEY_CURRENT_USER registry hive, rather than using the cached location from when the process was initially invoked (Failing to call this method explains the "Access is denied" exception I was receiving previously. The impersonated user was attempting to load the HKCU hive for the invoker's account, which is obviously not allowed)
RegDisablePredefinedCache();
Next, we need to load that account's profile before entering the impersonated thread. This ensures that the impersonated account's registry hive is available in memory. We call the LogonUser() and LoadUserProfile() COM methods in order to accomplish that:
// Get a token for the user
const int LOGON32_LOGON_BATCH = 4;
const int LOGON32_PROVIDER_DEFAULT = 0;
//We'll use our extension method to pass the password as a normal string
LogonUser(ImpUser, ImpDomain, ImpPassword.ToUnSecureString(), LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, ref Token);
// Load user profile
ProfileInfo profileInfo = new ProfileInfo();
profileInfo.dwSize = Marshal.SizeOf(profileInfo);
profileInfo.lpUserName = ImpUser;
profileInfo.dwFlags = 1;
bool loadSuccess = LoadUserProfile(Token, ref profileInfo);
//Detect and handle failure gracefully
if (!loadSuccess)
{
errCode = Marshal.GetLastWin32Error();
Win32Exception ex = new Win32Exception(errCode);
throw new Exception($"Failed to load profile for {ImpUser}. Error code: {errCode}", ex);
}
if (profileInfo.hProfile == IntPtr.Zero)
{
errCode = Marshal.GetLastWin32Error();
Win32Exception ex = new Win32Exception(errCode);
throw new Exception($"Failed accessing HKCU registry for {ImpUser}. Error code: {errCode}", ex);
}
Finally, thanks to one of the comments left on this question, I discovered a nifty nuget package called SimpleImpersonation. This obfuscates away most of the complexity involved with account impersonation:
//Note that UserCredentials() constructor I chose requires the
//password to be passed as a SecureString object.
var Credentials = new UserCredentials(impDomain, impUser, impPassword);
Impersonation.RunAsUser(Credentials, LogonType.Batch, () =>
{
//Within this bock, you can call methods such as
//Registry.CurrentUser.OpenSubKey()
//and they use the impersonated account's registry hive
}

Related

How deleted, replaced file at share folder

Hello I have a problem with deleting and replacing files at share folder
I have full access.
"access to the path was denied"
The copy function works well in both directions.
(but I have to delete files manually).
SERVER - >>> PC
SERVER <<< - PC
The program connects to the file server under another user and manipulate with files (copy files worked fine, delete files doesn't work replace doesn't work).
private void replaceFile(string source, string sourceName ,string destination)
{
try
{
using (new Impersonator(username(), domain(), password()))
{
//File.delete(destination);
//File.Copy(source, destination, true);
File.Replace(source, destination, null);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
private void copyFile(string source, string destination)
{
try
{
using (new Impersonator(username(), domain(), password()))
{
File.Copy(source, destination, true);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Windows.Forms;
namespace IFS_FileManager
{
public enum LogonType
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK = 3,
LOGON32_LOGON_BATCH = 4,
LOGON32_LOGON_SERVICE = 5,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT = 8, // Win2K or higher
LOGON32_LOGON_NEW_CREDENTIALS = 9 // Win2K or higher
};
public enum LogonProvider
{
LOGON32_PROVIDER_DEFAULT = 0,
LOGON32_PROVIDER_WINNT35 = 1,
LOGON32_PROVIDER_WINNT40 = 2,
LOGON32_PROVIDER_WINNT50 = 3
};
public enum ImpersonationLevel
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3
}
class Win32NativeMethods
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int LogonUser(string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
}
public class Impersonator : IDisposable
{
private WindowsImpersonationContext _wic;
public Impersonator(string userName, string domainName, string password, LogonType logonType, LogonProvider logonProvider)
{
Impersonate(userName, domainName, password, logonType, logonProvider);
}
public Impersonator(string userName, string domainName, string password)
{
Impersonate(userName, domainName, password, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT);
}
public Impersonator() { }
public void Dispose()
{
UndoImpersonation();
}
public void Impersonate(string userName, string domainName, string password)
{
Impersonate(userName, domainName, password, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT);
}
public void Impersonate(string userName, string domainName, string password, LogonType logonType, LogonProvider logonProvider)
{
UndoImpersonation();
IntPtr logonToken = IntPtr.Zero;
IntPtr logonTokenDuplicate = IntPtr.Zero;
try
{
// revert to the application pool identity, saving the identity of the current requestor
_wic = WindowsIdentity.Impersonate(IntPtr.Zero);
// do logon & impersonate
if (Win32NativeMethods.LogonUser(userName,domainName,password,(int)logonType,(int)logonProvider,ref logonToken) != 0)
{
if (Win32NativeMethods.DuplicateToken(logonToken, (int)ImpersonationLevel.SecurityImpersonation, ref logonTokenDuplicate) != 0)
{
var wi = new WindowsIdentity(logonTokenDuplicate);
wi.Impersonate();
// discard the returned identity context (which is the context of the application pool)
}
else
MessageBox.Show("\n " + Marshal.GetLastWin32Error(), "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
MessageBox.Show("\n " + Marshal.GetLastWin32Error(), "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
if (logonToken != IntPtr.Zero)
Win32NativeMethods.CloseHandle(logonToken);
if (logonTokenDuplicate != IntPtr.Zero)
Win32NativeMethods.CloseHandle(logonTokenDuplicate);
}
}
/// <summary>
/// Stops impersonation.
/// </summary>
private void UndoImpersonation()
{
// restore saved requestor identity
if (_wic != null)
_wic.Undo();
_wic = null;
}
}
}

use Process.Start while Impersonating (Window Application)

I'm trying to use Process.Start() under Impersonation, i have google for few days, most answer i come across was under ASP.net, but I'm developing for Window Application, so I'm having difficulty to find the root cause.
This is my impersonate code
private void impersonateValidUser(string userName, string domain, string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if ( LogonUser( userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
{
tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
mImpersonationContext = tempWindowsIdentity.Impersonate();
}
}
}
and i'm trying to open document through my program (none .exe, such as .txt, .doc)
using (new Impersonator(DomainUserID, Domain, Password))
Process.Start(filePath);
So far I'm able to detect the directory/file with the impersonate user, which suppose to be invisible to my current login user since i did not grant it the access. But whenever i try to open the document, i get error
System.ComponentModel.Win32Exception (0x80004005): Access is denied
Please correct me if I'm wrong, so in this scenario, I'm not suppose to set the UseShellExecute to false (and processStartInfo with username and password input too?) because it's for executable file(?), and i do come across with CreateProcessAsUser function as well, and this function doesn't seems to be applicable to my case too, from the example i read it's for the .exe file too.
Would be appreciate if anyone could enlightenment me.
update: impersonate class
public class Impersonator : IDisposable
{
#region P/Invoke
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int LogonUser( string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int DuplicateToken( IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool CloseHandle(IntPtr handle);
#endregion
#region Constants
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
#endregion
#region Attributes
private WindowsImpersonationContext mImpersonationContext = null;
#endregion
#region Public methods.
public Impersonator( string userName, string domainName, string password)
{
impersonateValidUser(userName, domainName, password);
}
#endregion
#region IDisposable member.
public void Dispose()
{
undoImpersonation();
}
#endregion
#region Private member.
private void impersonateValidUser(string userName, string domain, string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if ( RevertToSelf() )
{
if ( LogonUser( userName, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
{
tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
mImpersonationContext = tempWindowsIdentity.Impersonate();
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
finally
{
if ( token != IntPtr.Zero )
{
CloseHandle( token );
}
if ( tokenDuplicate != IntPtr.Zero )
{
CloseHandle( tokenDuplicate );
}
}
}
/// <summary>
/// Reverts the impersonation.
/// </summary>
private void undoImpersonation()
{
if ( mImpersonationContext != null )
{
mImpersonationContext.Undo();
}
}
#endregion
}
You can't use UseShellExecute = true when impersonating. This is related to the way how shell execution works in Windows. Basically everything is passed to the shell which looks up how to handle the verb ("open" in your case) and then starts the application under the user owning the shell, which is not the impersonated user - the impersonated user doesn't actually have a shell if there is no session!
Although you use a different mechanism for impersonating a user the documentation for UseShellExecute still applies in your case:
UseShellExecute must be false if the UserName property is not null or an empty string, or an InvalidOperationException will be thrown when the Process.Start(ProcessStartInfo) method is called.
To solve this issue it might be the easiest to look up the registered application yourself as described in this answer: Finding the default application for opening a particular file type on Windows. With the path to the associated application you can then start the executable as the other user.
Here is the complete class I use to impersonate user:
/// <summary>
/// Provide a context to impersonate operations.
/// </summary>
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public class Impersonate : IDisposable
{
/// <summary>
/// Initialize a new instance of the ImpersonateValidUser class with the specified user name, password, and domain.
/// </summary>
/// <param name="userName">The user name associated with the impersonation.</param>
/// <param name="password">The password for the user name associated with the impersonation.</param>
/// <param name="domain">The domain associated with the impersonation.</param>
/// <exception cref="ArgumentException">If the logon operation failed.</exception>
public Impersonate(string userName, SecureString password, string domain)
{
ValidateParameters(userName, password, domain);
WindowsIdentity tempWindowsIdentity;
IntPtr userAccountToken = IntPtr.Zero;
IntPtr passwordPointer = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if (Kernel32.RevertToSelf())
{
// Marshal the SecureString to unmanaged memory.
passwordPointer = Marshal.SecureStringToGlobalAllocUnicode(password);
if (Advapi32.LogonUser(userName, domain, passwordPointer, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref userAccountToken))
{
if (Advapi32.DuplicateToken(userAccountToken, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
return;
}
}
}
}
}
finally
{
// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode(passwordPointer);
// Close the token handle.
if (userAccountToken != IntPtr.Zero)
Kernel32.CloseHandle(userAccountToken);
if (tokenDuplicate != IntPtr.Zero)
Kernel32.CloseHandle(tokenDuplicate);
}
throw new ArgumentException(string.Format("Logon operation failed for userName {0}.", userName));
}
/// <summary>
/// Reverts the user context to the Windows user represented by the WindowsImpersonationContext.
/// </summary>
public void UndoImpersonation()
{
impersonationContext.Undo();
}
/// <summary>
/// Releases all resources used by <see cref="Impersonate"/> :
/// - Reverts the user context to the Windows user represented by this object : <see cref="System.Security.Principal.WindowsImpersonationContext"/>.Undo().
/// - Dispose the <see cref="System.Security.Principal.WindowsImpersonationContext"/>.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (impersonationContext != null)
{
//UndoImpersonation();
impersonationContext.Dispose();
impersonationContext = null;
}
}
}
private void ValidateParameters(string userName, SecureString password, string domain)
{
if (string.IsNullOrWhiteSpace(userName))
{
throw new ArgumentNullException("userName");
}
if (password == null || password.Length == 0)
{
throw new ArgumentNullException("password");
}
if (string.IsNullOrWhiteSpace(userName))
{
throw new ArgumentNullException("domain");
}
}
private WindowsImpersonationContext impersonationContext;
private const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
}
how to convert a string to a secure string :
var secure = new SecureString();
foreach (char c in "myclearpassword")
{
secure.AppendChar(c);
}
SO You can use it like that:
using (var imp = new Impersonate(DomainUserID, SecurePassword, Domain))
{
Process.Start(filePath);
}

LogonUser function fails with error code 0 in c#

My objective is to copy a folder from my system to a remote computer in c#.
I searched everywhere and found some information on how to do that. I am calling the LogonUser function with the domain, username and password, but it it fails and returns 0.
below is my piece of code. Can you please help me figure out the problem?
class Program
{
#region Assembly Functions
[DllImport("advapi32.dll")]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr handle);
#endregion
static void Main(string[] args)
{
SafeTokenHandle safeTokenHandle;
WindowsImpersonationContext impersonatedUser = null;
WindowsIdentity newId;
IntPtr tokenHandle;
IntPtr userHandle = IntPtr.Zero;
tokenHandle = IntPtr.Zero;
Console.WriteLine("Enter your domain.");
string domain = Console.ReadLine();
Console.WriteLine("Enter you user name.");
string uname = Console.ReadLine();
Console.WriteLine("Enter your password (Caution, password won't be hidden).");
string password = Console.ReadLine();
const int LOGON32_PROVIDER_DEFAULT = 0;
//This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
bool logon = LogonUser(uname, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out safeTokenHandle);
if (false == logon)
{
int ret = Marshal.GetLastWin32Error();
Console.WriteLine("LogonUser failed with error code : {0}", ret);
throw new System.ComponentModel.Win32Exception(ret);
}
if (logon)
{
newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());
using (impersonatedUser = newId.Impersonate())
{
// Check the identity.
Console.WriteLine("After impersonation: "
+ WindowsIdentity.GetCurrent().Name);
}
File.Copy(#"c:\result.xml", #"C:\result.xml", true);
}
//Undo impersonation
if (impersonatedUser != null)
{
impersonatedUser.Undo();
}
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
}
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle()
: base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
Why not use "Windows API Code Pack 1.1" which shows (among other things) how to use the shell to do e.g. drag and drop - which is what you essentially try to do.
If using the Shell, you don't have to think about how to logon and 1000 other things needed to support various situations.
Download the "Windows API Code Pack 1.1" package and look for DragAndDropWindow sample or some of the other samples.

C# File system on remote server in ActiveDirectory and Impersonation

This theme are not new. But I need help of some professional.
I am making a windows form application which will run on local system(not domain's computer). The application would creating folder and some files on Active Directory domain's shared folder.
I have read about Impersonation and have tried to make some thing like this: Impersonation by using LogonUser
Then I wrote the next code:
using System.Security.Principal;
using System.Runtime.InteropServices;
public partial class Form1 : Form
{
private enum LogonSessionType : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
private enum LogonProvider : uint
{
Default = 0,
WinNT35,
WinNT40,
WinNT50
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonSessionType logonType,
LogonProvider logonProvider,
out IntPtr token);
public Form1()
{
InitializeComponent();
}
protected void btnConnect_Click(object sender, EventArgs e)
{
IntPtr token = IntPtr.Zero;
WindowsImpersonationContext impersonateUser = null;
try
{
bool result = LogonUser("Administrator#mydomain.ru", "192.168.1.1", "SomeP#ssWorD",
LogonSessionType.Network, LogonProvider.Default, out token);
if(result)
{
WindowsIdentity id = new WindowsIdentity(token);
impersonateUser = id.Impersonate();
string showtext = string.Format("Identity: {0}", WindowsIdentity.GetCurrent().Name);
MessageBox.Show(showtext);
}
else
{
string showtext = string.Format("Identity: {0}", "Fail");
MessageBox.Show(showtext);
}
}
catch
{
}
finally
{
if(impersonateUser!=null)
impersonateUser.Undo();
if (token != IntPtr.Zero)
CloseHandle(token);
}
}
}
But bool result is always = false. What am I doing wrong?
i was wrong about understanding of LogonUser function. I was thinking that this function get remote token, but it generate.
Here is the right using:
bool result = LogonUser("Administrator", "mydomain.ru", "SomePa$$worD", LogonSessionType.NewCredentials, LogonProvider.Default, out safeTokenHandle);

Unable to find an entry point named 'LogonUser' in DLL 'advapi32.dll' Impersonation exception

I'm getting the following exception when going through some legacy impersonation logic:
Unable to find an entry point named 'LogonUser' in DLL 'advapi32.dll'
I understand that the error means that my app can't find the LogonUser method in the advapi32.dll.
The code looks something like this:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
if(LogonUser(_username, _domainname, _password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref _tokenHandle))
{
//do stuff...
}
Has anyone had a similar error - any suggestions on how to fix it or why it is happening? Is there a better way to do this besides using the advapi32.dll (its a .net 3.5 solution but there are lots of legacy classes)?
Maybe it has something to do with the "ExactSpelling = true"
This seems to work:
public enum LogonType : int
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkCleartText = 8,
NewCredentials = 9,
}
public enum LogonProvider : int
{
Default = 0,
}
public class Impersonation : IDisposable
{
#region Dll Imports
[DllImport("kernel32.dll")]
private static extern Boolean CloseHandle(IntPtr hObject);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool LogonUser(string username, string domain,
string password, LogonType logonType,
LogonProvider logonProvider,
out IntPtr userToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DuplicateToken(IntPtr token, int impersonationLevel,
ref IntPtr duplication);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool ImpersonateLoggedOnUser(IntPtr userToken);
#endregion
#region Private members
private bool _disposed;
private WindowsImpersonationContext _impersonationContext;
#endregion
#region Constructors
public Impersonation(String username, String domain, String password)
{
IntPtr userToken = IntPtr.Zero;
IntPtr userTokenDuplication = IntPtr.Zero;
// Logon with user and get token.
bool loggedOn = LogonUser(username, domain, password,
LogonType.Interactive, LogonProvider.Default,
out userToken);
if (loggedOn)
{
try
{
// Create a duplication of the usertoken, this is a solution
// for the known bug that is published under KB article Q319615.
if (DuplicateToken(userToken, 2, ref userTokenDuplication))
{
// Create windows identity from the token and impersonate the user.
WindowsIdentity identity = new WindowsIdentity(userTokenDuplication);
_impersonationContext = identity.Impersonate();
}
else
{
// Token duplication failed!
// Use the default ctor overload
// that will use Mashal.GetLastWin32Error();
// to create the exceptions details.
throw new Exception("Could not copy token");
}
}
finally
{
// Close usertoken handle duplication when created.
if (!userTokenDuplication.Equals(IntPtr.Zero))
{
// Closes the handle of the user.
CloseHandle(userTokenDuplication);
userTokenDuplication = IntPtr.Zero;
}
// Close usertoken handle when created.
if (!userToken.Equals(IntPtr.Zero))
{
// Closes the handle of the user.
CloseHandle(userToken);
userToken = IntPtr.Zero;
}
}
}
else
{
throw new Exception("Login failed");
}
}
~Impersonation()
{
Dispose(false);
}
#endregion
#region Public methods
public void Revert()
{
if (_impersonationContext != null)
{
// Revert to previous user.
_impersonationContext.Undo();
_impersonationContext = null;
}
}
#endregion
#region IDisposable implementation.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
Revert();
_disposed = true;
}
}
#endregion
}
Have you tried using the version of LogonUser's signature provided on pinvoke.net?

Categories

Resources