Ed Beck
Microsoft Developer Support Messaging Team
May 1998
Updated November 1998
Security Considerations
Application Considerations
Network Architecture
Your Decision Tree
Application Requirements
The Basics
Examples
Writing Your ASP
Troubleshooting and Debugging
Common MAPI Errors
References
Click to view or copy the sample files associated with this technical article.
This paper presents an overview of what you need to think about when designing a messaging application to be run from an Active Server Page (ASP). Since this paper is meant to be an overview, there are several references to other publications that contain more detailed information about the topic being covered. This article serves as an introduction to the considerations and technologies involved in messaging from the Web.
Security must be one of your first considerations when designing a messaging application for use on an ASP. There are three major components to be taken into consideration: Windows NT® security, Internet Information Server (IIS) security, and Exchange Server security. These must be set up so that they do not conflict with each other. Depending on your network architecture, you will be able to make use of different security features.
Two excellent articles on the MSDN Library provided much of the background information for this section. They are "Implementing a Secure Site with ASP" by Paul Enfield and "Authentication and Security for Internet Developers" by Scott Stabbert. Parts of this article were directly copied from those papers.
This paper assumes that you are using Windows NT 4.0 Server with Service Pack 3 (SP3) on an NTFS formatted hard drive. This part of the article will discuss the basics of NTLM. How NTLM relates to IIS will be discussed in "IIS Security" below.
NTLM stands for Windows NT LAN Manager because it was developed and originally used in Microsoft LAN Manager, one of Microsoft's earliest networking products. NTLM assumes that the user has logged onto a Windows NT network from a client and has been authenticated by a Domain Controller. A very important part of NTLM is that the user's password never goes over the network. Instead, when a client attempts to use a resource on a Windows NT Server, the following routine takes place:
IIS is just a tool to provide files to browsers. Every time you request a file from a Windows NT machine, a security check occurs. Therefore, IIS must implement some way of providing files to:
In the case of providing files to a Domain user, the process outlined above in the NTLM section could provide files to the user. But if you want to provide files to users who do not have a domain account, you need an account that is accessible from the Internet and will grant access to Internet users. IIS uses three authentication methods: Anonymous, NTLM, and Basic.
The way to provide access to nondomain users is that IIS sets up an account such that any user who hits a Web page is logged into an account that can access the Web. Whether or not you as the network administrator choose to use this account is up to you. You can decide to not allow anonymous access to your Web, or you can decide to create your own account to allow users anonymous access.
The Internet Service Manager allows you to change the authentication options and will be discussed later. For a more detailed discussion of IIS security, please see "Implementing a Secure Site with ASP" by Paul Enfield.
When IIS is set up on your server, an account named "IUSR_ComputerName" is created. For example, if your IIS machine is named "WebHost", then an account named "IUSR_WebHost" is created. This account is assigned a random password. IIS also sets the "Allow anonymous access" option to "On" by default. If you do nothing else, anonymous users will have access to your Web. This creates a problem with messaging applications that will be discussed later.
Another option is that you can create your own user account to access your Web. Reasons to create an account include:
In order to create your own anonymous user, you need to set up several components of Windows NT, Exchange, and Internet Information Server (IIS) correctly. This example creates a new user on the domain using User Manager for Domains, the Microsoft Exchange Administrator, and the Internet Service Manager.
Username: | AnonUser |
Full Name: | Anonymous User. |
Description: | For anonymous Web access. |
Password: | Fill in an appropriate password. |
Confirm Password: | Repeat password. |
User Must Change Password at Next Logon: | OFF |
User Cannot Change Password | ON* |
Password Never Expires: | ON* |
Account Disabled: | OFF |
Groups—Member of: | Domain Users and Domain Guests. |
Profiles: | User Profiles or Home Directory settings are not required. |
Hours: | No settings are required. |
Logon to: | Set as appropriate. |
Account—Account Expires | Never. |
Account—Account Type | Global Account for regular user accounts in the domain. |
Dialin—Grant dialin permissions to user | OFF |
Dialin—Call Back | No Call Back. |
*You may set these values to OFF. If you set these values OFF, you need to make sure to keep the password that IIS has in synch with the password that the Windows NT account has.
First Name: | Anonymous |
Last Name: | User |
Display: | Anonymous User |
Alias: | AnonUser |
Primary NT Account: | DOMAIN\AnonUser |
Select your Web and set the following properties of the Directory Security tab:
In IIS, NT Challenge Response refers to NTLM authentication. In NTLM authentication, the IIS machine challenges the client to hash a random value. IIS sends the same random value to the Primary Domain Controller. It then compares the results of the Client and the PDC hash. If the hashes match, the client is authenticated. This is how authentication works on a Windows NT domain.
The problem with this authentication is that it requires the user to have an account on the NT domain. Because most Internet developers want to allow the world to access their Web, "Anonymous" authentication was developed to allow unknown users onto a Web.
In Basic authentication, IIS challenges the client to provide the DOMAIN\UserID and password. In this case, the password goes over the wire in Base 64. You would want to use Basic authentication when you need the IIS computer to know the user's password. This is necessary in any case where you want IIS to access resources on another Windows NT server.
Opening a user's Exchange account located on a different server is an excellent example of why you would need to use Basic authentication.
When a browser tries to access a file, in this case an ASP, IIS determines who has access rights to the file in a certain order. This decision tree will play a large part in how you set up your messaging application.
The Exchange Server is designed to maintain security so that users cannot access mailboxes to which they do not have access rights.
To guard against unauthorized access, Exchange uses NTLM. That means that whenever a client tries to access an Exchange mailbox, Exchange challenges the client to hash a random value. Exchange will send the same random value to the PDC to hash. Exchange will then compare the encrypted tokens. If they match, then Exchange will allow the client into the mailbox.
Now that we've discussed security, it's time to talk about some of the things we need to consider in designing our application. By this, I'm referring as much to network architecture as to the way that you want to write your application.
One of the first questions you need to address is whether or not you want users to send their password from the client's browser to the IIS computer. If your application is going to be used in an intranet solution, then you may decide that it is OK for users to enter their password for transmission across the intranet. On the other hand, if you are designing an application for use on the Internet, I would not recommend designing your application such that users will need to send their password over the Internet to gain access to their mail account.
You also need to consider whether you want to identify who is sending the mail. If you want the user to access his or her Exchange mailbox, then the user will have to be identified. On the other hand, if you just want mail to generate mail and you do not need to know who it came from, then you do not need to identify your user.
Your network architecture must reflect what your primary concerns are. Your two choices are to have IIS and Exchange Server on the same computer or to have them on separate computers. There are advantages and disadvantages to both. The way your network needs to be set up is dependent on your answer to the above questions.
If you require the ability to:
then you must have IIS and Exchange Server on the same computer. Here's why:
The inescapable facts are that Exchange Server needs an account to send mail from, and that it uses NTLM to authenticate the user. This means that IIS needs to authenticate the user to Exchange. IIS authenticated the user when the user requested access to a Windows NT resource (the ASP). When this happened (assuming that "Allow Anonymous" is not on), IIS sent a random value to the client. The client returned the domain, UserID, and the random value hash. IIS sent the domain, UserID, and random value to a domain controller. The domain controller used that information to hash the random value and sent it back to IIS. IIS then compared the two hashes. If they were the same, then it granted the user access to the resource.
Now that IIS has authenticated the user, it can authenticate the user to Exchange Server only if IIS and Exchange are on the same computer. IIS cannot authenticate the user to an Exchange Server on another computer because IIS does not know the user's password.
If you are designing a mail system that will be open to the Internet, then security should be a bigger concern. Designing a system that requires users to send their password over the Internet might not be a good idea.
If, on the other hand, you are running in the confines of an intranet where you feel comfortable passing users' passwords over the wire, then you have the option of having IIS and Exchange on separate computers.
It has been my experience that most companies have their IIS and Exchange Servers on different computers. In this configuration, if you want users to access their own Exchange mailbox from an ASP, they will need to enter their password in order for IIS to authenticate them to Exchange Server.
If you are designing a mail system to be used exclusively on your intranet, you may decide that sending a user's password over the wire is not a big security concern. In this case you will be able to separate IIS and Exchange. This would allow you to distribute the load over more servers and have a server designed to do one job.
Given the above information, you need to decide how you are going to set up your application and your network. The following two illustrations are flow charts that you can use to help determine how to set up your application. The first flow chart assumes that you have total control over both the network architecture and the application design. Its purpose is to help you determine how to design your network to meet your application requirements.
The second flow chart assumes that you are locked into a network architecture and helps you determine how to set up your Web based on what your network will allow.
Besides setting up your architecture and configuring your servers, you need to decide how to design your application. This decision should be based upon what you want your application to do.
If you are creating a rather simple application that receives recipient, subject, and text from a user and simply sends the mail, this can be accomplished by using a couple of files. I use an HTM file to collect information from the user, then post the information to an ASP file, which sends the message.
You can also use straight ASP code to retrieve information about users from the address book and can even incorporate a filter so you limit the amount of information that the address book returns.
Alternatively, if you plan on designing a complex application that makes intelligent decisions, for example, routing a message to a particular manager depending on what information the user provided, then you may want to write a Visual Basic® DLL for the messaging part of the application. This would be advantageous because you can early bind your objects in Visual Basic, saving memory, and you will be able to reuse the code in other applications.
In order to send mail from an ASP you need to create a Session, a Message, and a Recipient. While you do not have to create an Outbox, it is often easier to understand if you explicitly create all the objects that you will use.
You need to create a session object that will perform your mail operations. This is the part of the application that will talk to Exchange Server.
The following line of code creates your MAPI Session:
Set objSession=CreateObject("MAPI.Session")
After your session is created, you will need to log onto it. You need to log onto the session either using the UserID of the person who has accessed the ASP, or with the account of the "Anonymous" user that you have defined and created an Exchange account for. Generally speaking, it is easier to create your login line to create a profile "on the fly." This profile is destroyed after the session is destroyed. Since this code is running on the server, it has the added advantage of not needing to store profiles on the server.
In order to create a profile on the fly, you need the UserID and the name of an Exchange Server on the same site as the user's mailbox. This information is concatenated into a string that is passed as the last parameter of the login command.
Fortunately, we can get this information without user intervention. You only need to have the name of an Exchange Server on the same site where your user's mailbox is located. Since you are writing an application for a particular Web to run on an Exchange mail system, I'll assume that you know the name of at least one Exchange server on the network.
StrServer="AnExchangeServer"
The mailbox will be the same as the UserID. If you are using an anonymous account, hardcode the mailbox name. The following code will retrieve the DOMAIN\UserID from ServerVariables and then manipulate the string to return just the UserID.
strLogonID = Request.ServerVariables("Logon_User")
strMailbox = Right(strLogonID, Len(strLogonID) - _
InStr(strLogonID, "\"))
Then put all this information together into the Logon statement:
strProfileInfo = strServer & vblf & strMailbox
objSession.Logon "", "", False, True, 0, True, _
strProfileInfo
For more information on each of the logon parameters, please refer to the MSDN Library or to the CDO Help file.
While it is not necessary to create the Outbox object in order to create a message, it is more clear.
Set objOutbox=objSession.Outbox
Add the message to the Outbox:
Set objMessage=ObjOutbox.Messages.Add
Add a recipient to the message:
Set objRecipient = objMessage.Recipients.Add
objRecipient.Name = strRecipient
objRecipient.Resolve
The only method required to send a mail message is the Send method:
ObjMessage.Send
You need to keep control of your objects or your server could crash. One problem is that if a user starts an instance of CDO.DLL on the server, then exits the application by destroying the browser, the session object could remain in memory, causing problems for subsequent users. This could even crash your server, necessitating a reboot. Because of this, creating a Web application with a global.asa file is recommended.
Global.asa contains scripts that affect the Application object and Session object. For example, Global.asa scripts make application- and session-scope variables available at startup. Global.asa should be present at the application root level for every ASP application.
Procedures declared in the Global.asa file can only be called from one or more of the scripts associated with the Application_OnStart, Application_OnEnd, Session_OnStart, and Session_OnEnd events. They are not available to the ASP pages in the ASP-based application.
Application_OnStart. This function runs when a user accesses a Web application that is not currently running. It is distinguished from Session_OnStart because it runs only the first time that an application is accessed. Use the Global.asa to create application-level variables and objects.
Application_OnEnd. This function runs when the last person exits an application. Use this function to clean up any application-level objects that you created in Application_OnStart.
Session_OnStart. This function runs when a new client accesses a Web. This function will run every time that a client accesses a Web. This is as opposed to the Application_OnStart, which runs when a client accesses an application that is not running. Use this function to create session-level variables and objects.
Session_OnEnd. This function runs when a client leaves a Web. It is very useful for catching when a user destroys their browser. Use this function to destroy any session-level objects you created in Session_OnStart.
An include file has an extension of “.inc” and is just a text file that contains script. You add the script to an ASP by using an “Include” line in the ASP.
<!--#include file="AnonLogon.inc"-->
Include files are useful because you can have ASP files that perform general tasks (like logging on to and logging off a MAPI.Session) and then include files that do more specific things, like send mail from a particular location or format.
You can download two sample files from the top of this article: AuthMail and AnonMail. Each .exe file contains the files that comprise a Web application.
This sample shows how to create an authenticated Active Messaging session, which will allow the user to access his or her own Exchange mailbox to send mail. Unless you have IIS and Exchange Server installed on the same computer, you will need to use "Basic" authentication to the Web. This is necessary so IIS can authenticate the user to Exchange Server.
File | Purpose |
AuthLogon.asp | This .asp file demonstrates using the functions in AuthLogon.inc. It causes an Active Messaging session to be created (or finds an existing Active Messaging session, if one has already been created by this user) and reads some properties from the session. The file then sends a message to your mailbox (it assumes that your UserID is your mailbox). |
AuthLogon.inc | This include file contains all of the VBScript code necessary to log on to an Exchange Server. It uses your UserID as your mailbox by pulling your UserID out of your LOGON_USER identification. |
SendMail.inc | This .inc file contains VBScript and HTML to retrieve recipient, subject, and message from the user, and sends the mail. |
AuthLogoff.asp | This .asp files causes the Active Server session to be abandoned. As a side effect, the Active Messaging session will also be destroyed. |
Global.asa | Active Server startup and shutdown functions. |
On the IIS computer:
Note If you have a "Remove" button, then you already have a Web application.
To run:
This sample shows how to create an anonymous Active Messaging session and send mail from it. In order for this application to work you must configure the anonymous user on your network by following these procedures:
File | Purpose | ||
AnonLogon.asp | This .asp file demonstrates using the functions in AnonLogon.inc. It causes an Active Messaging session to be created (or finds an existing Active Messaging session if one has already been created by this user) and reads some properties from the session. The file then calls SendMail.inc, which has a function that both collects data from the user and sends the mail to the recipient. | ||
AnonLogon.inc | This include file contains all of the VBScript code necessary to log on to an Exchange Server account. The main difference between this file and AuthLogon.inc (an example of an authenticated logon) is that it does not check your authentication, and it requires you to code in the mailbox you want to send mail from. | ||
SendMail.inc | This .inc file contains VBScript and HTML to retrieve recipient, subject, and message from the user, and sends the mail. | ||
AnonLogoff.asp | This .asp files causes the Active Server session to be abandoned. As a side effect, the Active Messaging session will also be destroyed. | ||
Global.asa | Active Server startup and shutdown functions. |
On the NT Domain:
On the IIS computer:
Note If you have a "Remove" button, then you already have a Web application.
I highly recommend the article by Roger Jack titled "Top Ten ASP Tips," published in the January 1998 issue of Microsoft Internet Developer (MIND). You can find it at http://www.microsoft.com/mind/0198/asptips/asptips.htm.
You can write your ASP and HTM in Notepad if you like. Save the files with the appropriate extension and save them to your Web.
I prefer to write my Web applications in Microsoft Visual InterDev™ for two reasons. I like the way it color codes. I can easily find a missing ">", and I can easily pick my VBScript code out from the rest of the code on the screen. The second reason is that I like the way I can use Visual InterDev to manage my Web, and that I can work on the same Web from different machines, and not have to keep track of what the latest file is. If I connect to my Web Visual InterDev and start to use a file, Visual InterDev will inform me that there is a newer version of the file available, and ask which one I want to use.
Unfortunately, at this time, debugging an application is not that easy. The debugging tools are not as robust as in Visual Basic or Visual C++®. This is one reason that if I have a complicated application, I’ll write it in Visual Basic, and then create a DLL for use by my ASP.
The method I employ most is to use Response.Write to find out where my application is and what it thinks is going on. The Response.Write line looks something like:
Response.Write(“Print out this string <br>”)
You can add variables to the statement as such:
Response.Write(“objSession.CurrentUser = “ & _
objSession.CurrentUser & “<br>”)
For brevity I use the following subroutine loaded in the head of my ASP files:
<%
Sub Write(strtext)
Response.Write(strText & "<br>")
End Sub
%>
Then I call it from within the script as such:
Write("ServerName = " & strServer)
Another thing I do is to load the following routine in the beginning of the ASP.
<TABLE BORDER=1 CELLPADDING=5 WIDTH=80%>
<TR><TD><B>Server Variable</B></TD><TD><B>Value</B></TD></TR>
<%
For Each name In Request.ServerVariables %>
<TR><TD><%=name%></TD>
<TD><%= Request.ServerVariables(name)%></TD>
</TR>
<% Next %>
</TABLE>
This routine prints out all of my server variables in a table. This is useful if I start to run into security problems. I can compare what the Web session variables are with what the messaging session variables are.
This section describes some common errors that are seen when you use CDO from an ASP. It provides the error code, the reason(s) for the error, and a brief description of how to resolve the problem. When available, KB articles are referenced. To check for more KB articles, please see http://support.microsoft.com/support/messagingsdk/faq/.
Error Message:
Collaboration Data Objects error '00000505'
You do not have permission to log on. [Microsoft Exchange Server Information Store - [MAPI_E_FAILONEPROVIDER(8004011D)]]
You are trying to access a mailbox that the ASP does not have proper permission for.
Check to make sure the ASP is being authenticated into the account you want. Insert this line into your VBScript to find out what account the ASP is running in:
Response.Write("You are logged on as " & Request.ServerVariables("LOGON_USER") & "<br>")
If you are logged in under the anonymous account, you will receive a blank string back.
You are using "NT Challenge Response" as your authentication type, and Netscape Navigator for your browser.
Netscape Navigator does not support "NT Challenge Response" as an authentication option. In order to log onto a user's Exchange account, the IIS authentication needs to be set up as "Basic (Clear Text)".
You are trying to gain programmatic access to Public Folders by assigning the folders collection to an object.
You can not gain programmatic access to a public folder from an ASP by stepping through the tree. You need to use GetFolder, which in turn requires you to know what the RootID of the Public Folder. You can use a MAPI property tag (for example, bstrPublicRootID = objInfoStore.Fields.Item( &H66310102 ).Value) to get the root ID of the Public Folders.
You are trying to access a mailbox that does not exist.
Check the name of the mailbox to make sure it exists.
Note You can successfully run the Logon line using a resource that does not exist. When you attempt to use a server object (for example, an Outbox or an Inbox) for the first time you will get this error.
This happens when you are attempting to access an Exchange mailbox that has been set up to allow Anonymous access to Exchange. This is most frequently done to allow users to send mail from a Web application using Microsoft Exchange where you do not need to or are unable to authenticate the user with Windows NT Challenge\Response Authentication (also referred to as NTLM) or Basic Authentication.
Internet Information Server (IIS) version 4.0 has an additional feature that allows for password synchronization between IIS 4.0 and the computer user accounts. Password synchronization should only be used with anonymous user accounts defined on the local computer, not with anonymous accounts remote computers. This feature will not work to authenticate an anonymous user to a remote Exchange server.
You must make sure that the account you are using to log onto Exchange has rights to log on locally. You do this through the User Manager. Select “Policies… User Rights”. Use the drop down to select “Log on Locally” and make sure that the account you want to use either has explicit rights, or is part of a group that has rights to log on locally. If the account does not have rights, add it using the “Add” button.
Cause:
Trying to access methods and properties without properly logging onto a session variable.
Make sure you have a valid logon before trying to access information store data.
See the KB article Q193451 PRB: "Error 8002009 - MAPI_E_NOT_INITIALIZED 80040605" w/ CDO for more information.
You are trying to use a resource that does not exist. This usually happens when you try to use a server or log onto a mailbox that does not exist. The most common cause of this is copying and pasting sample code without modifying the server and mailbox variables. Check your sample code for variables that are used in the Session Logon line. Most ASP sample code contains a variable called "strProfileInfo". This is a combination of the server name, a line feed, and the mailbox name. Look for the variables that define the server and mailbox, and make sure that the resources are on your network.
Make sure that the recipient is as specified. If you pass an invalid recipient to CDO in an ASP the only way to resolve the problem is to reenter the recipient. You can not call up the Address Book on the client.
You need to use the ProfileInfo parameter on the Logon call instead of a specific profile. This allows you to create a profile "on the fly" for the user of the ASP.
The reason for this is that the user will not normally have profiles on the server machine, and the profiles are probably not going to be loaded into HKEY_CURRENT_USER in order to be found by CDO.
Paul Enfield, "Implementing a Secure Site with ASP."
Scott Stabbert, "Authentication and Security for Internet Developers."