Использование олицетворения для приложения VB.NET WinForm, может сохранить файл, не может открыть файл - PullRequest
0 голосов
/ 20 февраля 2019

Я работаю над добавлением функции загрузки документов в приложение, которое написал.Я хочу, чтобы пользователь мог загружать, открывать и удалять документы на сетевом диске, к которым у него нет нормального доступа.Имея это в виду, я наткнулся на Impersonation, где пользователь может выдать себя за учетную запись пользователя, имеющего полные права на диск, а затем избавиться от него после выполнения кода.

Я никогда раньше не использовал олицетворениеПоэтому во время моего исследования я обнаружил эту тему: Олицетворение пользователя Windows или Active Directory из другого ненадежного домена

Я создал и скопировал класс, который пользователь Макс Вернон опубликовал следующим образом:

Option Explicit On
Option Infer Off

Imports System
Imports System.Runtime.InteropServices '   DLL Import
Imports System.Security.Principal '  WindowsImpersonationContext
Imports System.ComponentModel

Public Class Impersonation

    'Group Type Enum
    Enum SECURITY_IMPERSONATION_LEVEL As Int32
        SecurityAnonymous = 0
        SecurityIdentification = 1
        SecurityImpersonation = 2
        SecurityDelegation = 3
    End Enum

    Public Enum LogonType As Integer

        'This logon type is intended for users who will be interactively using the computer, such as a user being logged on
        'by a terminal server, remote shell, or similar process.
        'This logon type has the additional expense of caching logon information for disconnected operations,
        'therefore, it is inappropriate for some client/server applications, such as a mail server.
        LOGON32_LOGON_INTERACTIVE = 2

        'This logon type is intended for high performance servers to authenticate plaintext passwords.
        'The LogonUser function does not cache credentials for this logon type.
        LOGON32_LOGON_NETWORK = 3

        'This logon type is intended for batch servers, where processes may be executing on behalf of a user without
        'their direct intervention. This type is also for higher performance servers that process many plaintext
        'authentication attempts at a time, such as mail or Web servers.
        'The LogonUser function does not cache credentials for this logon type.
        LOGON32_LOGON_BATCH = 4

        'Indicates a service-type logon. The account provided must have the service privilege enabled.
        LOGON32_LOGON_SERVICE = 5

        'This logon type is for GINA DLLs that log on users who will be interactively using the computer.
        'This logon type can generate a unique audit record that shows when the workstation was unlocked.
        LOGON32_LOGON_UNLOCK = 7

        'This logon type preserves the name and password in the authentication package, which allows the server to make
        'connections to other network servers while impersonating the client. A server can accept plaintext credentials
        'from a client, call LogonUser, verify that the user can access the system across the network, and still
        'communicate with other servers.
        'NOTE: Windows NT:  This value is not supported.
        LOGON32_LOGON_NETWORK_CLEARTEXT = 8

        'This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
        'The new logon session has the same local identifier but uses different credentials for other network connections.
        'NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
        'NOTE: Windows NT:  This value is not supported.
        LOGON32_LOGON_NEW_CREDENTIALS = 9

    End Enum

    Public Enum LogonProvider As Integer

        'Use the standard logon provider for the system.
        'The default security provider is negotiate, unless you pass NULL for the domain name and the user name
        'is not in UPN format. In this case, the default provider is NTLM.
        'NOTE: Windows 2000/NT:   The default security provider is NTLM.
        LOGON32_PROVIDER_DEFAULT = 0
        LOGON32_PROVIDER_WINNT35 = 1
        LOGON32_PROVIDER_WINNT40 = 2
        LOGON32_PROVIDER_WINNT50 = 3

    End Enum

    'Obtains user token.
    Declare Auto Function LogonUser Lib "advapi32.dll" (ByVal lpszUsername As String, ByVal lpszDomain As String, ByVal lpszPassword As String, ByVal dwLogonType As LogonType, ByVal dwLogonProvider As LogonProvider, ByRef phToken As IntPtr) As Integer


    'Closes open handles returned by LogonUser.
    Declare Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Boolean

    'Creates duplicate token handle.
    Declare Auto Function DuplicateToken Lib "advapi32.dll" (ExistingTokenHandle As IntPtr, SECURITY_IMPERSONATION_LEVEL As Int16, ByRef DuplicateTokenHandle As IntPtr) As Boolean

    'WindowsImpersonationContext newUser.
    Private newUser As WindowsImpersonationContext

    'Attempts to impersonate a user.  If successful, returns 
    'a WindowsImpersonationContext of the new user's identity.
    ' 
    'Username that you want to impersonate.
    'Logon domain.
    'User's password to logon with.
    Public Sub Impersonator(ByVal sDomain As String, ByVal sUsername As String, ByVal sPassword As String)

        'Initialize tokens
        Dim pExistingTokenHandle As New IntPtr(0)
        Dim pDuplicateTokenHandle As New IntPtr(0)

        If sDomain = "" Then
            sDomain = System.Environment.MachineName
        End If

        Try

            Const LOGON32_PROVIDER_DEFAULT As Int32 = 0
            Const LOGON32_LOGON_NEW_CREDENTIALS = 9

            Dim bImpersonated As Boolean = LogonUser(sUsername, sDomain, sPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, pExistingTokenHandle)

            If bImpersonated = False Then
                Dim nErrorCode As Int32 = Marshal.GetLastWin32Error()
                Throw New ApplicationException("LogonUser() failed with error code: " & nErrorCode.ToString)
            End If

            Dim bRetVal As Boolean = DuplicateToken(pExistingTokenHandle, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, pDuplicateTokenHandle)
            If bRetVal = False Then
                Dim nErrorCode As Int32 = Marshal.GetLastWin32Error
                CloseHandle(pExistingTokenHandle)
                Throw New ApplicationException("DuplicateToken() failed with error code: " & nErrorCode)
            Else
                Dim newId As New WindowsIdentity(pDuplicateTokenHandle)
                Dim impersonatedUser As WindowsImpersonationContext = newId.Impersonate
                newUser = impersonatedUser
            End If

        Catch ex As Exception
            MessageBox.Show("An error has occurred. Please contact Technical Support. " & vbCrLf & ex.Message, "Application Title", MessageBoxButtons.OK, MessageBoxIcon.Error)
        Finally
            If pExistingTokenHandle <> IntPtr.Zero Then
                CloseHandle(pExistingTokenHandle)
            End If
            If pDuplicateTokenHandle <> IntPtr.Zero Then
                CloseHandle(pDuplicateTokenHandle)
            End If
        End Try
    End Sub

    Public Sub Undo()
        newUser.Undo()
    End Sub
End Class

Олицетворение отлично подходит для «загрузки» (фактически, просто копируя файл из локальных файлов пользователя на сетевой диск, создавая определенный путь к файлу, если он не существует), но не делает этого.Кажется, что работает при попытке открыть файл обратно или удалить указанный файл.

Я получаю ошибку отказа в доступе, как это:

Сообщение об ошибке при попытке открыть файл

Error Message When Trying to Open File

Событие клика по открытому файлу и вызов класса выглядят следующим образом:

 Private Sub btnOpenDoc_Click(sender As Object, e As EventArgs) Handles btnOpenDoc.Click
        Dim Impersonator As New Impersonation
        Dim sUser As String = "UserNameGoesHere"
        Dim sPass As String = "PasswordGoesHere"
        Dim sDomain As String = "DomainGoesHere"

        Try
            If sActionID <> "" And iDocument = 1 Then
                'Starts impersonation
                Impersonator.Impersonator(sDomain, sUser, sPass)

                Process.Start(RetrieveFilePath())

                'Ends Impersonation
                Impersonator.Undo()
            End If

        Catch ex As Exception
            MessageBox.Show("An error has occurred. Please contact Technical Support. " & vbCrLf & ex.Message, "Application Title", MessageBoxButtons.OK, MessageBoxIcon.Error)
            modGlobal.WriteToErrorLog(ex.Message, "frmActionEntry", modGlobal.GetExceptionInfo(ex), "frmActionEntry->btnOpenDoc_Click", currentUser.getEmployeeName())
        End Try
    End Sub

Вот функция удаления документа:

    Private Function DeleteFile() As Boolean
        Dim Impersonator As New Impersonation
        Dim sUser As String = "UsernameGoesHere"
        Dim sPass As String = "PasswordGoesHere"
        Dim sDomain As String = "DomainGoesHere"

        Try
            'Starts impersonation
            Impersonator.Impersonator(sDomain, sUser, sPass)

            File.Delete(RetrieveFilePath())
            Return True

            'Ends Impersonation
            Impersonator.Undo()
        Catch ex As Exception
            MessageBox.Show("An error has occurred. Please contact Technical Support. " & vbCrLf & ex.Message, "Application Title", MessageBoxButtons.OK, MessageBoxIcon.Error)
            modGlobal.WriteToErrorLog(ex.Message, "frmActionEntry", modGlobal.GetExceptionInfo(ex), "frmActionEntry->DeleteFile", currentUser.getEmployeeName())
            Return False
        End Try
    End Function

Он используется в основном аналогично в функции FileSave.Как я уже говорил, я новичок в подражании и чувствую, что ударился о стену, исследуя и пробуя разные вещи все утро.Любой совет очень ценится!

-Леви

1 Ответ

0 голосов
/ 25 февраля 2019

Итак, после долгих исследований, проб и ошибок у меня есть ответ на этот вопрос.

Краткий ответ:

Не существует чистого, элегантного способа использовать олицетворение для открытия файла всетевой диск, потому что вы либо сталкиваетесь с безопасностью Windows, либо сталкиваетесь с проблемами с оболочкой Windows.Я решил пойти другим путем.

Длинный ответ:

Я считаю, что я был прав в том, что ошибка «Доступ запрещен» был из-за попытки открыть файл от имени другого пользователя на локальном компьютере.компьютер пользователя.Чтобы обойти это, я решил попробовать и с помощью ProcessStartInfo () передать правильные учетные данные (при этом также используя олицетворение для доступа к диску), например:

'Opens the document associated with this action
    Private Sub btnOpenDoc_Click(sender As Object, e As EventArgs) Handles btnOpenDoc.Click
        'Initializes an impersonation object
        Dim Impersonator As New Impersonation
        'Strings with login credentials
        Dim sUser As String = "UsernameGoesHere"
        Dim sPass As String = "PasswordGoesHere"
        Dim sDomain As String = "DomainGoesHere"
        'Used to load file path in from RetrieveFilePath()
        Dim sPath As String = ""

        Try
            If sActionID <> "" And iDocument = 1 Then
                'Starts impersonation
                Impersonator.Impersonator(sDomain, sUser, sPass)

                'Initializes a ProcessStartInfo Object to use with impersonation
                'as Process.Start class always inherits the security context of 
                'the parent process i.e. the local user
                Dim startInfo As New ProcessStartInfo()

                'Creates a secure string as the startInfo.Password parameter only accepts SecureStrings
                Dim securePass As New Security.SecureString()

                'You can't put a full string into a SecureString, so appending char by char
                For Each c As Char In sPass
                    securePass.AppendChar(c)
                Next

                'Grab the file path
                sPath = RetrieveFilePath()

                'Load in the parameters for startInfo
                startInfo.FileName = sPath
                startInfo.UserName = sUser
                startInfo.Password = securePass
                startInfo.Domain = sDomain
                startInfo.UseShellExecute = False
                startInfo.WorkingDirectory = "\\Directory\Goes Here"

                If File.Exists(sPath) Then
                    'Execute the process using startInfo
                    Process.Start(startInfo)
                Else
                    MsgBox("File Not Found!")
                End If

                'Dispose of securePass
                securePass.Dispose()

                'Ends Impersonation
                Impersonator.Undo()
            End If

        Catch ex As Exception
            MessageBox.Show("An error has occurred. Please contact Technical Support. " & vbCrLf & ex.Message, "Application Title", MessageBoxButtons.OK, MessageBoxIcon.Error)
            modGlobal.WriteToErrorLog(ex.Message, "frmActionEntry", modGlobal.GetExceptionInfo(ex), "frmActionEntry->btnOpenDoc_Click", currentUser.getEmployeeName())
        End Try
    End Sub

Здесь следует отметить некоторые интересные аспекты.Вы должны использовать SecureString для пароля при использовании ProcessStartInfo, и его можно назначать только для символа, и что более важно, мне пришлось установить для свойства UseShellExecute значение False.

Я надеялся, что это будет работать, но посленекоторые итерации застряли в этом сообщении об ошибке:

Пример сообщения об ошибке

Я понял, что это из-за невозможности доступа к Windows Shell для поиска программы по умолчаниюоткрыть соответствующий тип файла с, так что он просто ожидал исполняемого файла.После дальнейших исследований мне не удалось найти чистый способ обойти это, поэтому я решил заняться этим файлом иным способом.

...