Search This Blog

Wednesday, October 07, 2009

ASP.net VB.Net access shared folder on computer not in domain

I had a web server that sat outside of a domain that needed to access a shared folder on a server within a domain. I didnot want to run the asp.net application in a domain user account, which would of been easier to setup, so I had to work out how to allow specific code to access the share.

My investigation led onto impersonation by using the winAPI function logonUser and also netUseAdd. Now as far as I can see NetUseAdd would be the more ideal solution but I am not a true developer and I could not find a good example of how to use this within vb.net in anasp.net page. So I went the logonuser route.

Now there are a whole number of different options with logonUser and really you need to to understand the windows security model to fully understand all the options. I include some of the articles I found that helped me tie down what I need to do, here

http://support.microsoft.com/kb/306158

http://www.pcreview.co.uk/forums/thread-3772715.php

http://msdn.microsoft.com/en-us/library/aa480587.aspx

Before delving into this I must state that I had to use mirrored accounts to get this working. This means that I had to create an identical user account on the separate web server to the account I wanted to use in the domain. (This goes back to my 2nd paragraph that states the netUseAdd would be ideal as this would of allowed me to not have to use impersonation and connect using the domain account and not use a mirrored account. If you know how to use netUseAdd from vb.net I love to know :o) ).

Now to get this working in my asp.net application I created a class, that contained the majority of the code in the MSDN article above (http://support.microsoft.com/kb/306158), the clas allows me to easy call on the code from any file.

File: App_code\UserImpersonation.vb

Imports Microsoft.VisualBasic
Imports System.Web
Imports System.Web.Security
Imports System.Security.Principal
Imports System.Runtime.InteropServices

Public Class UserImpersonation

    Const LOGON32_LOGON_INTERACTIVE = 2
    Const LOGON32_LOGON_NETWORK = 3
    Const LOGON32_LOGON_BATCH = 4
    Const LOGON32_LOGON_SERVICE = 5
    Const LOGON32_LOGON_UNLOCK = 7
    Const LOGON32_LOGON_NETWORK_CLEARTEXT = 8
    Const LOGON32_LOGON_NEW_CREDENTIALS = 9
    Const LOGON32_PROVIDER_DEFAULT = 0
    Const LOGON32_PROVIDER_WINNT35 = 1
    Const LOGON32_PROVIDER_WINNT40 = 2
    Const LOGON32_PROVIDER_WINNT50 = 3

    Dim impersonationContext As WindowsImpersonationContext

    Declare Function LogonUserA 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 Integer

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

    Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long
    Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long

    Public Function impersonateUser(ByVal userName As String, ByVal domain As String, ByVal password As String) As Boolean
        Return impersonateValidUser(userName, domain, password)
    End Function

    Public Sub undoimpersonateUser()
        undoImpersonation()
    End Sub

    Private Function impersonateValidUser(ByVal userName As String, ByVal domain As String, ByVal password As String) As Boolean

        Dim tempWindowsIdentity As WindowsIdentity
        Dim token As IntPtr = IntPtr.Zero
        Dim tokenDuplicate As IntPtr = IntPtr.Zero
        impersonateValidUser = False

        If RevertToSelf() Then
            If LogonUserA(userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, token) <> 0 Then
                If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
                    tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
                    impersonationContext = tempWindowsIdentity.Impersonate()
                    If Not impersonationContext Is Nothing Then
                        impersonateValidUser = True
                    End If
                End If
            End If
        End If
        If Not tokenDuplicate.Equals(IntPtr.Zero) Then
            CloseHandle(tokenDuplicate)
        End If
        If Not token.Equals(IntPtr.Zero) Then
            CloseHandle(token)
        End If
    End Function

    Private Sub undoImpersonation()
        impersonationContext.Undo()
    End Sub

End Class

Now the call from the code behind page

Dim impersonateUser As New UserImpersonation

impersonateUser.impersonateUser("username", "", "Password")

‘Code utilising impersonation, nothing special here just your code, I create folders here on the network share.

impersonateUser.undoimpersonateUser()

Now there is really one main line in the class file

LogonUserA(userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, token)

Now this is setup to work in my scenario of a server outside the domain, but it should also work in other scenarios you just have to configure the

LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50 constants.

Now I don;t specify a domain in the

impersonateUser.impersonateUser("username", "", "Password") call, this is show the account get authenticated locally on the webserver. But in a different scenario you may want to specify a domain.

OK thats it, as I have said I am not a developer (or class myself as one) so I may not have this fully correct, and if so I apologise just hope this gets someone up and running.

Now if only I could get NetUseAdd() working……


Share/Bookmark

No comments:

Post a Comment