This code works fine with me:
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr token);
enum LogonType
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkClearText = 8,
NewCredentials = 9
}
enum LogonProvider
{
Default = 0,
WinNT35 = 1,
WinNT40 = 2,
WinNT50 = 3
}
private void Button1_Click()
{
IntPtr token = IntPtr.Zero;
LogonUser("Administrator",
"192.168.1.244",
"PassWord",
(int)LogonType.NewCredentials,
(int)LogonProvider.WinNT50,
ref token);
using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(token))
{
CloseHandle(token);
/*
Code_of_Do_Something
*/
}
}
BUT...This means I have to repeat the last code which inside "Button1_Click()" each time I need to do impersonation ( Doing something on the remote machine = server).
So my question: Is it possible to do something like this illustration?:
You can use delegates for this purpose. The easiest way is to use Action or Func. If you don't need a return value, use an Action:
private void RunImpersonated(Action act)
{
IntPtr token = IntPtr.Zero;
LogonUser("Administrator",
"192.168.1.244",
"PassWord",
(int)LogonType.NewCredentials,
(int)LogonProvider.WinNT50,
ref token);
try
{
using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(token))
{
// Call action
act();
}
}
finally
{
CloseHandle(token);
}
}
Note that there are lots of variations with generic type parameters that allow you to also provide parameters to Action or Func delegates in a strongly typed way. If you need an into parameter for instance, use Action<int> instead of just Action.
Also note that I created a finally block that closes the handle whether an exception occurs or not.
In order to call the function, you can use a lambda expression:
private void button1_Click(object sender, EventArgs e)
{
RunImpersonated(() => {
DirectoryInfo dir = new DirectoryInfo( #"\\192.168.1.244\repository");
foreach (DirectoryInfo di in dir.GetDirectories())
{
lable_folders_count.Text = Convert.ToString(dir.GetFileSystemInfos().Length);
}
});
}
Yes, it is possible to pass code as a parameter. But let's solve your problem without using lambdas:
private void Button1_Click()
{
using(GetImpersonationContext())
{
/* code here */
}
}
private WindowsImpersonationContext GetImpersonationContext()
{
IntPtr token = IntPtr.Zero;
LogonUser("Administrator",
"192.168.1.244",
"PassWord",
(int)LogonType.NewCredentials,
(int)LogonProvider.WinNT50,
ref token);
WindowsImpersonationContext context = WindowsIdentity.Impersonate(token);
CloseHandle(token);
return context;
}
Related
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
}
I'm developing windows service for downloading and printing documents(pdf, excel. word) under different credentials (need to add document in printing queue under different users). When I'm impersonate user I get an exception "Settings to printer are not valid". When I run service under my own context there is no exceptions and its works as expected
Here is windows service configuration:
var exitCode = HostFactory.Run(x =>
{
x.Service<PrinterCentralPointService>(service =>
{
service.ConstructUsing(printerService => new PrinterCentralPointService());
service.WhenStarted(printerService => printerService.Start());
service.WhenStopped(printerService => printerService.Stop());
});
x.RunAsLocalSystem();
});
int exitCodeValue = (int)Convert.ChangeType(exitCode, exitCode.GetTypeCode());
Environment.ExitCode = exitCodeValue;
Service:
public class PrinterCentralPointService {
private readonly Timer _timer;
public PrinterCentralPointService() {
_timer = new Timer(1000) { AutoReset = false };
_timer.Elapsed += TimerElapsed;
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
var ps = new PrinterSettings(); // works fine
using (var i = new ImpersonatedUser("login", "domain", "password"))
{
var ps = new PrinterSettings(); // default printer is not defined and etc... // ... aspose code to send to printer
}
}
public void Start() {
_timer.Start();
}
public void Stop() {
_timer.Stop();
}
}
And ImpersonatedUser.cs
public class ImpersonatedUser: IDisposable
{
IntPtr userHandle;
WindowsImpersonationContext impersonationContext;
public ImpersonatedUser(string user, string domain, string password)
{
userHandle = IntPtr.Zero;
bool loggedOn = LogonUser(
user,
domain,
password,
LogonType.Batch,
LogonProvider.Default,
out userHandle);
if (!loggedOn)
throw new Win32Exception(Marshal.GetLastWin32Error());
impersonationContext = WindowsIdentity.Impersonate(userHandle);
}
public void Dispose()
{
if (userHandle != IntPtr.Zero)
{
CloseHandle(userHandle);
userHandle = IntPtr.Zero;
impersonationContext.Undo();
logger.Debug("Finished Impersonating user");
}
}
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
LogonType dwLogonType,
LogonProvider dwLogonProvider,
out IntPtr phToken
);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hHandle);
enum LogonType: int
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
NetworkCleartext = 8,
NewCredentials = 9,
}
enum LogonProvider: int
{
Default = 0,
}
}
What am I doing wrong?
i am trying to implement a custom PSHost class for the purpose of crafting GUIs for Power Shell scripts.
I have taken the code here as a base and started adapting it for GUI Projects
https://msdn.microsoft.com/en-us/library/ee706557%28v=vs.85%29.aspx
All was fine until i tried to run the Get-Credential cmdlet and got a error that the method is not implemented(ooouuuu)...
The initial code was these two methods:
public override PSCredential PromptForCredential(
string caption,
string message,
string userName,
string targetName)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
public override PSCredential PromptForCredential(
string caption,
string message,
string userName,
string targetName,
PSCredentialTypes allowedCredentialTypes,
PSCredentialUIOptions options)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
So after some research i implemented the dialogue like this:
[DllImport("ole32.dll")]
public static extern void CoTaskMemFree(IntPtr ptr);
[DllImport("credui.dll", CharSet = CharSet.Auto)]
private static extern int CredUIPromptForWindowsCredentials(ref CREDUI_INFO notUsedHere, int authError, ref uint authPackage, IntPtr InAuthBuffer, uint InAuthBufferSize, out IntPtr refOutAuthBuffer, out uint refOutAuthBufferSize, ref bool fSave, int flags);
[DllImport("credui.dll", CharSet = CharSet.Auto)]
private static extern bool CredUnPackAuthenticationBuffer(int dwFlags, IntPtr pAuthBuffer, uint cbAuthBuffer, StringBuilder pszUserName, ref int pcchMaxUserName, StringBuilder pszDomainName, ref int pcchMaxDomainame, StringBuilder pszPassword, ref int pcchMaxPassword);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct CREDUI_INFO
{
public int cbSize;
public IntPtr hwndParent;
public string pszMessageText;
public string pszCaptionText;
public IntPtr hbmBanner;
}
private enum PromptForWindowsCredentialsFlags
{
...
}
public override PSCredential PromptForCredential(
string caption,
string message,
string userName,
string targetName,
PSCredentialTypes allowedCredentialTypes,
PSCredentialUIOptions options)
{
CREDUI_INFO credui = new CREDUI_INFO();
credui.pszCaptionText = "Please enter the credentails";
credui.pszMessageText = "DisplayedMessage";
credui.cbSize = Marshal.SizeOf(credui);
uint authPackage = 0;
IntPtr outCredBuffer = new IntPtr();
uint outCredSize;
bool save = false;
int result = CredUIPromptForWindowsCredentials(ref credui, 0, ref authPackage, IntPtr.Zero, 0, out outCredBuffer, out outCredSize, ref save, 1 /* Generic */);
var usernameBuf = new StringBuilder(100);
var passwordBuf = new StringBuilder(100);
var domainBuf = new StringBuilder(100);
int maxUserName = 100;
int maxDomain = 100;
int maxPassword = 100;
if (result == 0)
{
if (CredUnPackAuthenticationBuffer(0, outCredBuffer, outCredSize, usernameBuf, ref maxUserName, domainBuf, ref maxDomain, passwordBuf, ref maxPassword))
{
//clear the memory allocated by CredUIPromptForWindowsCredentials
CoTaskMemFree(outCredBuffer);
SecureString secureString = new SecureString();
foreach (char c in passwordBuf.ToString())
{
secureString.AppendChar(c);
}
return new PSCredential(usernameBuf.ToString(), secureString);
}
}
return null;
}
This works fine but there is one snag, when running the Get-Credential cmdlet it prompts for a value for the Credential parameter, regardless of any input after hitting return the dialogue pops up and everything works as expected.
Doing the same in ISE the dialogue pops up directly without any prompt.
I thought it was due to the arguments of the method but making them optional by defining a default value did not make a difference.
It might be due to how the cmdlet is defined in the PS environment so my question is:
How can i make it so that running the cmdlet will jump straight to the dialogue?
Is it possible to define a default value for the Credential parameter to bind to? I'm guessing this is how ISE gets around this or am i missing something?
I took some time and experimenting but i found it, the mandatory parameter binding is handled by the public override Dictionary<string, PSObject> Prompt(string caption, string message, Collection<FieldDescription> descriptions) method in the PSHostUserInterface interface before PromptForCredential is called.
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);
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?