Manage IIS 6 in .NET

by Marcel Wijnands 20. August 2008 00:19

If you ever needed to automate the creation of web applications, application pools, virtual directories, etc. you might find this IIsManager ClassLibrary I made to be very useful. Using the System.DirectoryServices namespace to manage IIS 6 is not very straightforward, so I decided to wrap IIS 6 into these classes;

  • IIsService
  • IIsApplicationPool
  • IIsSite
  • IIsDirectory
  • IIsFile
  • IIsVirtualDirectory

After instantiating the IIsService class, you can use it to iterate through sites (using LINQ if you want), adding or removing them, and set properties on them.

The following example will create a website, set it to use ASP.NET 2.0, create an application pool and set the website to use it, changes access permissions on a directory and creates a virtual directory containing a web application.

' Instantiate an IIsService which represents the W3SVC service on the localhost.
Using IIsSvc = New IIsService

    ' Creates an IIsSite.
    Using testsite = IIsSvc.AddSite("TestSite", "c:\inetpub\testsite", "test.site.nl")

        ' Sets AccessPermissions to allow reading and executing scripts (.aspx).
        testsite.AccessPermissions = AccessPermissionFlags.Read + AccessPermissionFlags.Script

        ' Sets the website to use ASP.NET 2.0
        testsite.ASPNETVersion = ASPNETVersions.v2_0_50727

        ' Create an IIsApplicationPool.
        Dim testpool = IIsSvc.AddAppPool("testpool")

        ' Set the site to use the new application pool.
        testsite.ApplicationPoolId = testpool.Id

        ' Creates an IIsDirectory and changes the AccessPermissions to allow writing to it.
        ' Note that the physical directory already has to exist since we're just creating
        ' metabase information.
        Using images = testsite.AddDirectory("images")
            images.AccessPermissions = AccessPermissionFlags.Read + AccessPermissionFlags.Write
        End Using

        ' Creates an IIsVirtualDirectory and creates a Web application in it.
        Using newapp = testsite.AddVirtualDirectory("newapp", "c\inetpub\newapp")
            newapp.CreateApplication()
            newapp.AccessPermissions = AccessPermissionFlags.Read + AccessPermissionFlags.Script
        End Using

        ' Starts the website. It is stopped at creation by default.
        testsite.StartSite()

    End Using

    ' Because the classes implement IDisposable, the Dispose method is automatically called
    ' at 'End Using'.
End Using

You can download the solution below. It's a Visual Studio 2008 solution, but the compiled assembly is also included. It contains some more examples for you to explain the usage of the ClassLibrary.

IIsManager.zip (65.34 kb)

The project is now also hosted on codeplex at http://www.codeplex.com/IIsManager.

Tags:

IIS 6 | VB.NET

Accessing network shares with ASP.NET

by Marcel Wijnands 5. August 2008 23:51

Recently, I had to develop a web application that had to grab pictures off a network share, manipulate them, and show them on a page. The users of this application would log in automatically with their Windows user credentials and based on the groups they were a member of, they would or would not see certain content.

To do this, I had to use the 'Integrated Windows Authentication' option in IIS and add <identity impersonate="true"/> to the Web.config.

The thing I noticed is that even though the logged on user had the required rights to access the network share, I would be getting 'Access denied' or  'Path not found' error messages.

After some research I found out that, using impersonation like this, the IIS worker process would get a so called 'impersonation token' of the logged on user, while the token needed to access network resources has to be a 'primary token'. One workaround is to add credentials to the impersonation tag, like <identity impersonation="true" userName="DOMAIN\User" password="Pass"/>. This would cause the IIS worker process to run all code in the security context of 'User' using a primary token.

This workaround was useless for my application because I needed to run most code in the context of the logged on user. What I needed was to get a primary token of a user that has access to the network share, and run pieces of code under the security context of this user.

To do this I wrote a little function to get a WindowsIndentity containing a primary token;

Dim LOGON32_LOGON_INTERACTIVE As Integer = 2
Dim LOGON32_PROVIDER_DEFAULT As Integer = 0

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 GetFixedIdentity(ByVal Domain As String, _
                                 ByVal UserName As String, _
                                 ByVal Password As String) As WindowsIdentity

    Dim tempIdentity As WindowsIdentity = Nothing
    Dim token As IntPtr = IntPtr.Zero
    Dim tokenDuplicate As IntPtr = IntPtr.Zero

    If RevertToSelf() Then
        If LogonUserA(UserName, Domain, Password, LOGON32_LOGON_INTERACTIVE, _
                      LOGON32_PROVIDER_DEFAULT, token) <> 0 Then

            If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
                tempIdentity = New WindowsIdentity(tokenDuplicate)
            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

    Return tempIdentity

End Function

Now, this piece of unmanaged code could be a performance hit if it is used often, because it will validate the user credentials against the domain controller every single time. I figured I only get this WindowsIdentity on application startup and keep it in the Application Class, so I added this in Global.asax.vb;

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)

    Application("FixedIdentity") = GetFixedIdentity("DOMAIN", "user", "pass")

End Sub

So whenever I needed access to a network share, I would run that piece of code in the ImpersonationContext of the WindowsIdentity I kept in the Application Class, like this;

Dim ImpersonationContext As WindowsImpersonationContext = Nothing

Try

    If Not Application("FixedIdentity") Is Nothing Then _
        ImpersonationContext = CType(Application("FixedIdentity"),  _
                                        WindowsIdentity).Impersonate

    'This code is executed by the Fixed Identity
    For Each File In Directory.GetFiles("\\SERVER02\D$\Images")

        Debug.WriteLine(File)

    Next

Catch ex As Exception

    Debug.WriteLine(ex.Message)

Finally

    ' This always has to be executed, so put it in Finally. Otherwise
    ' you will keep executing code as the Fixed Identity.
    If Not ImpersonationContext Is Nothing Then ImpersonationContext.Undo()

End Try

Now you will be able to access the share, and be happy! :D

Tags:

ASP.NET | IIS 6 | VB.NET

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen | Modified by Mooglegiant