在MC ++中使用LogonUser失败

时间:2009-05-26 13:58:00

标签: c++ visual-studio-2008 login

在与这个战斗了一个星期后,我没有真正理解为什么它在我的代码中经常失败,但在其他示例中却没有。我的代码在编译时不会登录到我知道具有正确登录信息的用户。它失败的地方是以下几行:

wi = gcnew WindowsIdentity(token);

此处失败,因为令牌为零,这意味着它永远不会设置为用户令牌。这是我的完整代码:

#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

现在使用Microsoft的这个略微修改的示例我能够获得登录和令牌:

#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

为什么微软的代码会成功,我的失败呢?

2 个答案:

答案 0 :(得分:1)

“为什么微软的代码会成功,我的失败?”

因为你的代码做了不同的事情: - )

在这一行:

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

请注意,'tokenHandle'是通过引用传递的,但在您的版本中:

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

通过引用传递'token',因此您的本地引用不会更新,这就是为什么当您将它传递给WindowsIdentity时它仍为零。

答案 1 :(得分:0)

我认为答案在MSDN文章中描述了LogonUser函数:


LOGON32_LOGON_NETWORK登录类型最快,但它有以下限制:

该函数返回模拟令牌,而不是主令牌。您不能直接在CreateProcessAsUser函数中使用此标记。但是,您可以调用DuplicateTokenEx函数将令牌转换为主令牌,然后在CreateProcessAsUser中使用它。