LogonUser


LogonUserメソッドとWindowsIdentityクラスのImpersonateメソッドを組み合わせることで
ログインしているユーザとは別のユーザとしてプログラムを実行させることができる(ユーザの偽装)



手順としては以下のようになる


1.LogonUserメソッドで別ユーザのトークンを取得
2.WindowsIdentityクラスのImpersonateメソッドで1で取得したトークンを用いて別ユーザに偽装
3.偽装したユーザで任意の処理を実行
4.WindowsIdentityクラスのUndoメソッドで元のユーザに戻る



サンプルプログラムは以下の通り


Module1.vb

Imports System
Imports System.Runtime.InteropServices
Imports System.Security.Principal
Imports System.Security.Permissions
Imports Microsoft.VisualBasic
<Assembly: SecurityPermissionAttribute(SecurityAction.RequestMinimum, UnmanagedCode:=True), _
 Assembly: PermissionSetAttribute(SecurityAction.RequestMinimum, Name:="FullTrust")> 
Module Module1

    Public Class ImpersonationDemo

        '必要なAPIの定義
        Private Declare Auto Function LogonUser Lib "advapi32.dll" (ByVal lpszUsername As [String], _
            ByVal lpszDomain As [String], ByVal lpszPassword As [String], _
            ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, _
            ByRef phToken As IntPtr) As Boolean

        <DllImport("kernel32.dll")> _
        Public Shared Function FormatMessage(ByVal dwFlags As Integer, ByRef lpSource As IntPtr, _
            ByVal dwMessageId As Integer, ByVal dwLanguageId As Integer, ByRef lpBuffer As [String], _
            ByVal nSize As Integer, ByRef Arguments As IntPtr) As Integer

        End Function

        Public Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Boolean


        Public Declare Auto Function DuplicateToken Lib "advapi32.dll" (ByVal ExistingTokenHandle As IntPtr, _
                ByVal SECURITY_IMPERSONATION_LEVEL As Integer, _
                ByRef DuplicateTokenHandle As IntPtr) As Boolean

        '---メイン処理---
        <PermissionSetAttribute(SecurityAction.Demand, Name:="FullTrust")> _
        Public Overloads Shared Sub Main(ByVal args() As String)

            Dim tokenHandle As New IntPtr(0)
            Dim dupeTokenHandle As New IntPtr(0)

            Try

                Dim userName, domainName, password As String

                '★偽装するユーザID、パスワードを定義 
                domainName = ""             'ドメインに所属していないならドメインは不要
                userName = "GisouTest"      'ユーザ
                password = "GisouTest"      'パスワード

                Const LOGON32_PROVIDER_DEFAULT As Integer = 0
                Const LOGON32_LOGON_INTERACTIVE As Integer = 2

                tokenHandle = IntPtr.Zero

                '■指定したユーザ情報でトークンを発行
                Dim returnValue As Boolean = LogonUser(userName, domainName, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, tokenHandle)

                Console.WriteLine("LogonUser called.")

                '■トークンが発行できたか確認
                If False = returnValue Then
                    Dim ret As Integer = Marshal.GetLastWin32Error()
                    Console.WriteLine("LogonUser failed with error code : {0}", ret)
                    Throw New System.ComponentModel.Win32Exception(ret)

                    Return
                End If

                Dim success As String
                If returnValue Then success = "Yes" Else success = "No"
                Console.WriteLine(("Did LogonUser succeed? " + success))
                Console.WriteLine(("Value of Windows NT token: " + tokenHandle.ToString()))


                ' 現在のユーザの確認1
                Console.WriteLine(("Before impersonation: " + WindowsIdentity.GetCurrent().Name))

                '■発行したトークンで別ユーザに偽装
                Dim newId As New WindowsIdentity(tokenHandle)
                Dim impersonatedUser As WindowsImpersonationContext = newId.Impersonate()

                '■偽装したユーザでファイルアクセス(偽装されたかのテスト)
                Call GetDirTest()

                ' 現在のユーザの確認2
                Console.WriteLine(("After impersonation: " + WindowsIdentity.GetCurrent().Name))

                '■ユーザの偽装をやめる
                impersonatedUser.Undo()

                ' 現在のユーザの確認3
                Console.WriteLine(("After Undo: " + WindowsIdentity.GetCurrent().Name))

                ' Free the tokens.
                If Not System.IntPtr.op_Equality(tokenHandle, IntPtr.Zero) Then
                    CloseHandle(tokenHandle)
                End If

            Catch ex As Exception
                Console.WriteLine(("例外 →" + ex.Message))
            End Try
        End Sub 'Main 
    End Class 'Class1

    Private Sub GetDirTest()

        '★IPアドレス
        Const SERVER_IP As String = "127.0.0.1"

        '★フォルダのパス(アクセス権のないフォルダだと例外が発生するのでそれでテストする)
        Const SERVER_DIRECTORY As String = "D:\Gisou\GisouTest\"

        'フォルダにアクセス
        Dim serverPath As String = System.IO.Path.Combine(SERVER_IP, SERVER_DIRECTORY)

        'サーバのディレクトリ内を走査
        For Each serverFilePath As String In System.IO.Directory.GetFiles(serverPath)

            'ファイル名を取得
            Dim fileName As String = System.IO.Path.GetFileName(serverFilePath)
            Console.WriteLine("fileName→ " + fileName)

        Next

    End Sub

End Module


このサンプルプログラムでは別ユーザに偽装したのちフォルダのアクセスを行っている
フォルダへのアクセス権を設定することで偽装できたかの確認ができる
(★の箇所を適当に書き換えること)



なお、LogonUserではローカルコンピュータにしかログオンできない
別のコンピュータへログインしたい場合は別の仕組みが必要


参考:LogonUserメソッドについて
http://msdn.microsoft.com/ja-jp/library/cc447468.aspx



動作確認環境:Windows XP sp3,Visual Stadio 2005