Сбой с LogonUser в MC ++ - PullRequest
0 голосов
/ 26 мая 2009

После борьбы с этим в течение недели я действительно нигде не понял, почему это постоянно терпит неудачу в моем коде, но не в других примерах. Мой код, который во время компиляции не будет регистрироваться у пользователя, который, как я знаю, имеет правильную информацию для входа. Где это терпит неудачу - следующая строка:

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

Почему код Microsoft должен быть успешным, а мой - нет?

Ответы [ 2 ]

1 голос
/ 26 мая 2009

«Почему код Microsoft должен быть успешным, а мой - нет?»

Потому что ваш код делает что-то другое: -)

В этой строке:

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());

вы не передаете 'токен' по ссылке, поэтому ваша локальная ссылка не будет обновлена, поэтому она по-прежнему равна нулю при передаче ее в WindowsIdentity.

0 голосов
/ 26 мая 2009

Я думаю, что ответ в статье MSDN, описывающей функцию LogonUser:


Тип входа LOGON32_LOGON_NETWORK самый быстрый, но имеет следующие ограничения:

Функция возвращает токен олицетворения, а не первичный токен. Вы не можете использовать этот токен непосредственно в функции CreateProcessAsUser. Однако вы можете вызвать функцию DuplicateTokenEx для преобразования токена в первичный токен, а затем использовать его в CreateProcessAsUser.


Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...