Error with LogonUser in MS ++

After battling this for a week, I got nowhere on why it fails consistently in my code, but not in other examples. My code, which, although it compiles, will not register with a user that I know has the correct login information. Where it fails is the following line:

wi = gcnew WindowsIdentity(token);

      

It doesn't work here because the token is zero, which means it has never been set to the user's token. Here is my complete 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 the login and 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 does Microsoft code have to succeed if my mistake?

0


a source to share


2 answers


"Why does Microsoft's code have to succeed if it's my fault?"

Because your code is doing something different :-)

In this line:

bool returnValue = LogonUser(userName, domainName, Console::ReadLine(), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
    &tokenHandle);

      



note that the 'tokenHandle' is passed by reference, but in your version:

bSuccess = LogonUser(_srUsername, _srDomain, _srPassword, 8, 0, (IntPtr*)token.ToPointer());

      

you are not passing a 'token' by reference, so your local reference will not be updated, so it is still null when you pass it to WindowsIdentity.

+1


a source


I think the answer is in the MSDN article describing the LogonUser function:


The LOGON32_LOGON_NETWORK login type is the fastest, but it has the following limitations:



The function returns the impersonation token, not the primary token. You cannot use this token directly in the CreateProcessAsUser function. However, you can call the DuplicateTokenEx function to convert the token to a primary token and then use it in CreateProcessAsUser.


0


a source







All Articles