Failure with LogonUser in MC++
- by Alikar
After fighting with this for a week I have not really gotten anywhere in why it constantly fails in my code, but not in other examples. My code, which while it compiles, will not log into a user that I know has the correct login information. Where it fails is the following line:
wi = gcnew WindowsIdentity(token);
It fails here because the token is zero, meaning that it was never set to a user token. Here is my full code:
#ifndef UNCAPI_H
#define UNCAPI_H
#include <windows.h>
#pragma once
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Security::Principal;
using namespace System::Security::Permissions;
namespace UNCAPI
{
public ref class UNCAccess
{
public:
//bool Logon(String ^_srUsername, String ^_srDomain, String ^_srPassword);
[PermissionSetAttribute(SecurityAction::Demand, Name = "FullTrust")]
bool Logon(String ^_srUsername, String ^_srDomain, String ^_srPassword)
{
bool bSuccess = false;
token = IntPtr(0);
bSuccess = LogonUser(_srUsername, _srDomain, _srPassword, 8, 0, &tokenHandle);
if(bSuccess)
{
wi = gcnew WindowsIdentity(token);
wic = wi->Impersonate();
}
return bSuccess;
}
void UNCAccess::Logoff()
{
if (wic != nullptr )
{
wic->Undo();
}
CloseHandle((int*)token.ToPointer());
}
private:
[DllImport("advapi32.dll", SetLastError=true)]//[DllImport("advapi32.DLL", EntryPoint="LogonUserW", SetLastError=true, CharSet=CharSet::Unicode, ExactSpelling=true, CallingConvention=CallingConvention::StdCall)]
bool static LogonUser(String ^lpszUsername, String ^lpszDomain, String ^lpszPassword, int dwLogonType, int dwLogonProvider, IntPtr *phToken);
[DllImport("KERNEL32.DLL", EntryPoint="CloseHandle", SetLastError=true, CharSet=CharSet::Unicode, ExactSpelling=true, CallingConvention=CallingConvention::StdCall)]
bool static CloseHandle(int *handle);
IntPtr token;
WindowsIdentity ^wi;
WindowsImpersonationContext ^wic;
};// End of Class UNCAccess
}// End of Name Space
#endif UNCAPI_H
Now using this slightly modified example from Microsoft I was able to get a login and a token:
#using <mscorlib.dll>
#using <System.dll>
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Security::Principal;
using namespace System::Security::Permissions;
[assembly:SecurityPermissionAttribute(SecurityAction::RequestMinimum, UnmanagedCode=true)]
[assembly:PermissionSetAttribute(SecurityAction::RequestMinimum, Name = "FullTrust")];
[DllImport("advapi32.dll", SetLastError=true)]
bool LogonUser(String^ lpszUsername, String^ lpszDomain, String^ lpszPassword, int dwLogonType, int dwLogonProvider, IntPtr* phToken);
[DllImport("kernel32.dll", CharSet=System::Runtime::InteropServices::CharSet::Auto)]
int FormatMessage(int dwFlags, IntPtr* lpSource, int dwMessageId, int dwLanguageId, String^ lpBuffer, int nSize, IntPtr *Arguments);
[DllImport("kernel32.dll", CharSet=CharSet::Auto)]
bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet=CharSet::Auto, SetLastError=true)]
bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, IntPtr* DuplicateTokenHandle);
// GetErrorMessage formats and returns an error message
// corresponding to the input errorCode.
String^ GetErrorMessage(int errorCode)
{
int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
//int errorCode = 0x5; //ERROR_ACCESS_DENIED
//throw new System.ComponentModel.Win32Exception(errorCode);
int messageSize = 255;
String^ lpMsgBuf = "";
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
IntPtr ptrlpSource = IntPtr::Zero;
IntPtr prtArguments = IntPtr::Zero;
int retVal = FormatMessage(dwFlags, &ptrlpSource, errorCode, 0, lpMsgBuf, messageSize, &prtArguments);
if (0 == retVal)
{
throw gcnew Exception(String::Format( "Failed to format message for error code {0}. ", errorCode));
}
return lpMsgBuf;
}
// Test harness.
// If you incorporate this code into a DLL, be sure to demand FullTrust.
[PermissionSetAttribute(SecurityAction::Demand, Name = "FullTrust")]
int main()
{
IntPtr tokenHandle = IntPtr(0);
IntPtr dupeTokenHandle = IntPtr(0);
try
{
String^ userName;
String^ domainName;
// Get the user token for the specified user, domain, and password using the
// unmanaged LogonUser method.
// The local machine name can be used for the domain name to impersonate a user on this machine.
Console::Write("Enter the name of the domain on which to log on: ");
domainName = Console::ReadLine();
Console::Write("Enter the login of a user on {0} that you wish to impersonate: ", domainName);
userName = Console::ReadLine();
Console::Write("Enter the password for {0}: ", userName);
const int LOGON32_PROVIDER_DEFAULT = 0;
//This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
const int SecurityImpersonation = 2;
tokenHandle = IntPtr::Zero;
dupeTokenHandle = IntPtr::Zero;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(userName, domainName, Console::ReadLine(),
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
&tokenHandle);
Console::WriteLine("LogonUser called.");
if (false == returnValue)
{
int ret = Marshal::GetLastWin32Error();
Console::WriteLine("LogonUser failed with error code : {0}", ret);
Console::WriteLine("\nError: [{0}] {1}\n", ret, GetErrorMessage(ret));
int errorCode = 0x5; //ERROR_ACCESS_DENIED
throw gcnew System::ComponentModel::Win32Exception(errorCode);
}
Console::WriteLine("Did LogonUser Succeed? {0}", (returnValue?"Yes":"No"));
Console::WriteLine("Value of Windows NT token: {0}", tokenHandle);
// Check the identity.
Console::WriteLine("Before impersonation: {0}", WindowsIdentity::GetCurrent()->Name);
bool retVal = DuplicateToken(tokenHandle, SecurityImpersonation, &dupeTokenHandle);
if (false == retVal)
{
CloseHandle(tokenHandle);
Console::WriteLine("Exception thrown in trying to duplicate token.");
return -1;
}
// The token that is passed to the following constructor must
// be a primary token in order to use it for impersonation.
WindowsIdentity^ newId = gcnew WindowsIdentity(dupeTokenHandle);
WindowsImpersonationContext^ impersonatedUser = newId->Impersonate();
// Check the identity.
Console::WriteLine("After impersonation: {0}", WindowsIdentity::GetCurrent()->Name);
// Stop impersonating the user.
impersonatedUser->Undo();
// Check the identity.
Console::WriteLine("After Undo: {0}", WindowsIdentity::GetCurrent()->Name);
// Free the tokens.
if (tokenHandle != IntPtr::Zero)
CloseHandle(tokenHandle);
if (dupeTokenHandle != IntPtr::Zero)
CloseHandle(dupeTokenHandle);
}
catch(Exception^ ex)
{
Console::WriteLine("Exception occurred. {0}", ex->Message);
}
Console::ReadLine();
}// end of function
Why should Microsoft's code succeed, where mine fails?