HOWTO: Using CDOEXM in ASP.NET (.NET Framework 2.0 Walkthrough)

I just finished updating a support document which walks through creating a simple COM+ component and ASP.NET page to illustrate how you have to use CDOEXM with ASP.NET due to impersonation limitations of CDOEXM...enjoy...

 

...Use all code in this post at your own risk, etc...  

 

CDO for Exchange Management (CDOEXM) is a management API for Exchange 2000 and 2003. While using CDOEXM in an ASP or ASPX web application you may get unexpected Access Denied or Catastrophic Failure errors. This is because CDOEXM only supports process-level impersonation and not the thread-level impersonation required by a web application using Windows Integrated for example. Due to impersonation limitations of CDOEXM, the only method supported by Microsoft for using CDOEXM from ASP or ASPX is to create a COM component and register that component with component services (COM+) running the component in a specific user identity.

Below is a walkthrough of how to create a simple .NET component which wraps CDOEXM and ADSI code to create users and associate mailboxes. There is also a function to illustrate the security context of the component which you can compare to the context of your application. We will illustrate this difference in a simple ASP.NET web application.

Environment Setup

This walkthrough is based off of Exchange 2003 and Visual Studio 2005 running off of Windows 2003. The code in this walkthrough is C#.

This code should be run from a Windows Server 2003 client that has Exchange 2003 Administrative Tools installed if you are targeting Exchange 2003 or an Exchange 2000 server. If you are targeting an Exchange 2000 server, you could also use a Windows 2000 client that has Exchange 2000 Administrative Tools installed. The CDOEXM version that is included in Exchange 2003 must be used when you access Exchange 2003. The Exchange 2003 CDOEXM can also be used to access Exchange 2000. The CDOEXM library that is included in Exchange 2000 is not supported for accessing Exchange 2003.

Creating the C# Component

Build and configure a COM+ component to host the CDOEXM code and ensure that it is running with the credentials of a service account and not inheriting any credentials from the caller.

Create the project…

  1. Start Visual Studio 2005
  2. Select File->New->Project…
  3. Under C#, select Windows
  4. In the “Template Types” select “Class Library”
  5. In the Name box type “CDOEXMComponent”
  6. Click OK.
  7. Add references to System.EnterpriseServices, System.DirectoryServices, CDOEXM (listed on the COM tab), and Active DS Type Library (also listed on the COM tab).

Sign and Create Interop Libraries for ActiveDS and CDOEXM…

1. Right click on the CDOEXMComponent project in the Solution Explorer and click properties. In the signing tab check the box to “Sign the assembly” and create a new strong name key called “CDOEXMComponent.snk”.

2. Still in the project properties window, select the Application tab and click “Assembly Information…”, check the box that says, “Make assembly COM-Visible”.

3. Open the AssemblyInfo.cs and add “using System.EnterpriseServices;” to the top and the following lines at the bottom of the file…

[assembly: ApplicationActivation(ActivationOption.Server)]

[assembly: ApplicationName("CDOEXMComponent")]

[assembly: Description("Simple CDOEXM Component Sample")]

Add the CDOEXMSample class…

  1. Select the “Solution Explorer” view tab.
    1. Rename the class1.cs file to “CDOEXMSample.cs”.
  2. Remove the code generated by the Wizard.
    1. Cut and past the code provided below:

using System;

using System.Collections.Generic;

using System.DirectoryServices;

using System.EnterpriseServices;

using System.Runtime.InteropServices;

using System.Text;

using ActiveDs;

using CDOEXM;

public class CDOEXMSample:

    System.EnterpriseServices.ServicedComponent

{

    public void CreateUserWithMailbox(string strFirstName,

            string strLastName, string strSamName,

            string strBaseContainer, string strHomeMDB)

    {

        //

        // Start local variable definitions

        //

        DirectoryEntry oCont =

            new DirectoryEntry("LDAP://" + strBaseContainer);

        IADsContainer oDSCont = null;

        IADsUser oUser = null;

        IMailboxStore oMailBox = null;

        try

        {

            oDSCont = oCont.NativeObject as IADsContainer;

            oUser = oDSCont.Create("user",

                string.Format("CN={0} {1}",

                strFirstName, strLastName)) as IADsUser;

            oUser.Put("sn", strLastName);

            oUser.Put("givenname", strFirstName);

            oUser.Put("samaccountname", strSamName);

            oUser.SetInfo();

            oUser.AccountDisabled = false;

            oUser.SetInfo();

            oMailBox = oUser as IMailboxStore;

            oMailBox.CreateMailbox(strHomeMDB);

            oUser.SetInfo();

        }

        finally

        {

            oCont.Dispose();

            if (oDSCont != null)

            {

                Marshal.ReleaseComObject(oDSCont);

                oDSCont = null;

            }

            if (oUser != null)

            {

                Marshal.ReleaseComObject(oUser);

                oUser = null;

            }

            if (oMailBox != null)

            {

                Marshal.ReleaseComObject(oMailBox);

                oMailBox = null;

            }

        }

    }

    public string GetIdentity()

    {

        AppDomain.CurrentDomain.SetPrincipalPolicy(

            System.Security.Principal.PrincipalPolicy.WindowsPrincipal);

        System.Security.Principal.WindowsPrincipal user =

            System.Threading.Thread.CurrentPrincipal

                as System.Security.Principal.WindowsPrincipal;

        return user.Identity.Name;

    }

}

Create and Configure the COM+ Component

1. Build the CDOEXMComponent project in Visual Studio

2. From the Visual Studio command prompt run “regsvcs CDOEXMComponent.dll”

3. Open “Component Services” from “Administrative Tools”

4. Under “Component Services”, “Computers”, “My Computer”, “COM+ Applications”, find “CDOEXMComponent” and right click on it then select “Properties”

5. On the security tab:

a. Under “Authorization” uncheck “Enforce access checks for this application”

b. Select “None” for “Authentication Level for Calls”

c. Select “Identify” for “Impersonation Level”

6. On the identity tab:

a. Configure “This user” as an account that has Exchange Admin privileges on your Exchange server. You could use the Administrator account for testing purposes.

7. Click OK

Create Web Application to Consume CDOEXMComponent

Create a folder and application files

1. Create a folder called “CDOEXMComponent” on the file system of your web server. For example, “C:\inetpub\wwwroot\CDOEXMComponent”.

2. Copy CDOEXMComponent.dll, Interop.ActiveDs.dll, and Interop.CDOEXM.dll from the output directory of the CDOEXMComponent project to a folder named “bin” under the folder you just created in Step 1.

3. Create a new file in the “CDOEXMComponent” folder called “web.config”

a. Copy the following XML and paste it in the web.config file

<?xml version="1.0"?>

<configuration>

    <appSettings/>

    <system.web>

      <identity impersonate="true"/>

      <compilation debug="true">

        <assemblies>

                        <add assembly="System.DirectoryServices,

                      Version=2.0.0.0,

                      Culture=neutral,

                      PublicKeyToken=B03F5F7F11D50A3A"/>

        </assemblies>

      </compilation>

        <authentication mode="Windows"/>

    </system.web>

</configuration>

4. Create another file in the “CDOEXMComponent” folder called “Default.aspx”

a. Copy the following code and paste it in the aspx file

b. Notice you will have to change the variables to match your test environment

<%@ Page Language="C#" %>

<%

    // CreateUserWithMailbox Input, change to match

    // your test environment.

    string firstName = "Matt";

    string lastName = "Stehle";

    string samName = "mstehle";

    string baseContainer = "CN=Users,DC=mstehle03,DC=extest,DC=microsoft,DC=com";

    string homeMDB = "CN=Mailbox Store (MSTEHLEEX03),CN=First Storage Group,CN=InformationStore,CN=MSTEHLEEX03,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=First Organization,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=mstehle03,DC=extest,DC=microsoft,DC=com";

    // Print out the security context of the web application

    Response.Write(string.Format(

        "<br><b>Web Application Context:</b> {0}<br>",

        System.Threading.Thread.CurrentPrincipal.Identity.Name));

    // Create component

    CDOEXMSample obj = new CDOEXMSample();

    try

    {

        obj.CreateUserWithMailbox(firstName, lastName,

            samName, baseContainer, homeMDB);

        Response.Write("<br>Mailbox Created!<br>");

    }

    catch (Exception ex)

    {

    Response.Write("<br>Error when creating mailbox...<br>");

        Response.Write(string.Format("Message: {0}<br>", ex.Message));

        Response.Write(string.Format("Stack Trace: {0}<br>", ex.StackTrace));

    }

    // Print out the security context of the component

    Response.Write(string.Format(

        "<br><b>CDOEXMComponent Context:</b> {0}<br><br>",

        obj.GetIdentity()));

%>

Create and Configure the virtual directory in IIS on your web server

1. Click Start, select “Run…” and type “inetmgr”, Click OK

2. Expand the “Web Sites” folder

3. Select a Web Site, right click and select New, “Virtual Directory…”

a. Click Next

b. For the alias, type “CDOEXMComponent”

c. Click Next

d. Set the appropriate path to the folder where you just created the ASPX file.

e. Click Next

f. Select Read and “Run scripts (such as ASP)”

g. Click Next

h. Click Finish

4. Right click the CDOEXMComponent virtual directory you just created and select Properties

a. Click the “Directory Security” tab

b. In “Authentication and access control” click the “Edit…” button

c. Uncheck “Enable anonymous access”

d. Check only “Integrated Windows Authentication” under “Authenticated access”

e. Click OK

f. Click OK

Talking a look at the results

When you access this page from another machine as different user than the administrator account configured in the COM+ component you will notice that the web application and the COM+ component are running in different security contexts. For example in my test, I was logged in a user called Green Giant and when I hit the web I got the following result…

Web Application Context: MSTEHLE03\ggiant

Mailbox Created!

CDOEXMComponent Context: MSTEHLE03\Administrator

…The key here is that Green Giant is just a simple User in my test domain. He has no permissions to create new Active Directory Users or Exchange mailboxes. However, all he has to do is authenticate against the domain to gain access to this web form. The web form makes the call to our COM+ component which always executes the CDOEXM code in the context of Administrator, not matter who the calling process is running as. Again, this is because CDOEXM does not support thread level impersonation.

Therefore it is very important to pay attention to the directory security configuration in IIS and what users will have access to the web application in this scenario as well as who has rights to create and make calls to the COM+ component. Anybody who can call this component can create a mailbox.