Environment-specific Design Issues

Applies To: Windows Server 2003 with SP1

Authorization Manager is designed for applications using the trusted subsystem application model, where access checks are done in the context of the server, and ACLs on back-end server resources only need to grant access to the application server service account. (For more information, see Trusted Subsystem Application Model, earlier in this paper.) To use a trusted subsystem model, usually the application server will run in the context of a dedicated service account. Since the application needs to read Authorization policy information from the Authorization Manager policy store, the security context that the application runs in needs Read permission to the store. You can do this by designating the dedicated service account as a Reader of the Authorization Manager policy store. To do this, you can use the Authorization Manager MMC (Microsoft Management Console).

If impersonation is used in the application, you should not impersonate a client while calling Authorization Manager API. The only exception to this is when you wish to initialize a client context from the impersonated thread context. In that case, call the IAzApplication::InitializeClientContextFromToken method and specify 0 for the ullTokenHandle parameter. The other Authorization Manager interfaces read the Authorization Manager authorization policy store (which contains user role information) and the IAzApplicatin::AccessCheck function runs BizRules and resolves LDAP query groups. Since a user requires a high degree of access to be able to read authorization policy and perform BizRule script actions, you should not call these functions while impersonating the security context of the user. Each Windows programming environment gives you different options to use to manage impersonation and call Authorization Manager from a service account for the application.

ASP.NET

Application servers written in ASP.NET can use the Authorization Manager API through the .NET Interop assembly that ships in the \AuthMan directory in the .NET Framework. To create a trusted subsystem model in ASP.NET, you need to run the application server in a service account. To configure your ASP.NET application server to run in a service account, you create a separate IIS 6.0 worker process which runs in the security context of the service account created for the application server. You can then configure your ASP.NET application to use this dedicated IIS worker process. For more information about IIS 6.0 worker processes, see the Microsoft Internet Information Services page on the Microsoft Web site at https://www.microsoft.com/technet/prodtechnol/windows2000serv/technologies/iis/default.mspx. Alternatively, you can use ASP.NET to configure the context in which an application runs using the Web.config file for your ASP.NET application. In that case, you should configure the ASP.NET application to run as the dedicated service account.

When your ASP.NET application is initialized, it can call AzAuthorizationStore.Initialize to connect to the Authorization Manager store. It then calls AzApplication.Initialize to load an initial percentage of the applications authorization policy.

At run time, when a client connects, you have the option of initializing a user context from the clients account name or from a token representing the client. The token can be retrieved in ASP.NET by way of the ASP.NET HttpWorkerRequest object. Authorization Manager can also initialize a context from a clients SID, though that is not usually available in ASP or ASP.NET. It is recommended that you initialize the Authorization Manager context from a token when possible because, when initializing from a token, you do not have to potentially query the users account object. That may require network-bound queries to Active Directory.

The following code calls the Authorization Manager API through ASP.NET, using a .NET Interop assembly to approve an expense approval. This code is a simple Web expense application that runs in a dedicated service account. The Authorization Manager policy store for this application can be created from the VBScript install script that appeared earlier in this paper.

<%@ Page Language="C#" Debug="True"%> 
<%@ Assembly Name ="Microsoft.Interop.Security.AzRoles"%> 
<%@ Import Namespace="Microsoft.Interop.Security.AzRoles"%> 
<%@ Import Namespace="System.Runtime.InteropServices" %> 
<html> 
   <head> 
   </head> 
   <body> 
      <% 
      Microsoft.Interop.Security.AzRoles.AzAuthorizationStoreClass 
AzManStore = 
            new 
Microsoft.Interop.Security.AzRoles.AzAuthorizationStoreClass(); 
      // Keep the authorization store in a safe place out of the Web 
space. 
      AzManStore.Initialize(0, 
@"msxml://D:\SecureDir\MyWebAppsAzStore.xml", 
                            null); 
      IAzApplication azApp = 
AzManStore.OpenApplication("expense",null); 
      HandleRef token = new HandleRef(this, ((HttpWorkerRequest) 
((IServiceProvider)Context).GetService(typeof(HttpWorkerRequest))).GetU 
serToken()); 
//-------------- Create Client Context -------------- 
      IAzClientContext context = 
azApp.InitializeClientContextFromToken( 
                        (UInt64)token.Handle, 
                        0); 
      object[] scope = new Object[1]; 
      scope[0] = ""; 
      object[] operations = new Object[1]; 
      operations[0] = 55; 
//--------------- Do access check -------------------- 
//Set up BizRule params. To optimize performance, name/value pairs must 
//be placed in array alphabetically 
      Object[] BRNames = new Object[1]; 
      BRNames[0] = "ExpAmount"; 
      Object[] BRValues = new Object[1]; 
      BRValues[0] = Convert.ToInt32(Request.QueryString["Amount"]); 
      object[] results = (object[]) context.AccessCheck("Approve", 
                                                        scope, 
                                                        operations, 
                                                        BRNames, 
                                                        BRValues, 
.null,null,null); 
      bool bAuthorized = true; 
      foreach (int iResCode in results) 
      { 
         if ( iResCode != 0 ) // zero = no error 
         { 
           bAuthorized = false; 
           break; 
         } 
      } 
      Response.Write((string)context.UserSamCompat + "<br>"); 
      Response.Write( 
             "Approval of " + Convert.ToString(BRValues[0]) + " was"); 
      if (bAuthorized) { 
         %><font color="#008000"><b>APPROVED</b></font><% 
      } else { 
         %><font color="#FF0000"><b>DENIED</b></font>.<% 
      } 
      %> 
   </body> 
</html> 

ASP

Applications that use Authorization Manager usually use the trusted subsystem model. By design, IIS Application Service Providers (ASP) applications impersonate the context of each connected client. To use a trusted subsystem model in ASP, you have two options:

Option 1: Use Internet Information Services 6.0 URL authorization and impersonate the Web expense service account.

Create a separate IIS 6.0 worker process which runs in a security context that is created for the ASP application. This runs an instance of IIS and ASP in a dedicated service account for the ASP application. Normally, ASP applications run impersonating the authenticated user. IIS 6.0 URL authorization allows you to configure the ASP application to run in the context of the IIS worker process. Since you set up the IIS worker process to run in the application service account, we can use IIS 6.0 URL authorization to run the ASP application in that security context. IIS integrated authentication still authenticates the client, but instead of impersonating the client, IIS 6.0 URL authorization causes the ASP application to run in the service account. When a client connects and is authenticated by IIS, you can use the ASP server variable AUTH_USER to retrieve the authenticated name of the client and call AzApplication.InitializeClientContextFromName to create an Authorization Manager context for the client. For more information about IIS 6.0 URL authorization, see IIS 6.0 URL Authorization later in this paper.

The following code is an ASP application, using Authorization Manager to validate an expense approval. This code assumes that the ASP application is impersonating a dedicated service account, not the client security context. The application is configured to impersonate the IIS worker process using IIS 6.0 URL authorization, as described above.

<%@ LANGUAGE="VBSCRIPT" %> 
<HTML> 
<HEAD> 
</HEAD> 
<BODY> 
<% 
Dim CCHandle 
Dim Results 
Dim Names(5) 
Dim Values(5) 
Dim Scopes(5) 
Dim Operations(10) 
Dim AzManStore 
Dim App 
Dim UserName 
Set AzManStore = CreateObject("Azroles.AzAuthorizationStore") 
'Set myUser = Session.GetObject("LDAP") 
UserName = Request.ServerVariables("LOGON_USER") 
AzManStore.Initialize 0, 
"msxml://D:\Inetpub\wwwroot\MyWebAppsAzStore.xml" 
set App = AzManStore.OpenApplication ("Expense") 
'--------------- Create Client Context -------------- 
Set CCHandle = App.InitializeClientContextFromName(UserName) 
'--------------- Do access check -------------------- 
'Set up BizRule params. To optimize performance, name/value pairs must 
be 
'placed in array alphabetically 
Names(0) = "ExpAmount" 
Values(0) = 0 
Names(1) = "Param2 for BizRule - Name" 
Values(1) = "Param2 for BizRule - value" 
Scopes(0) = Empty 
Operations(0) = 55 
If Len(Request.QueryString("amount")) > 9 Then 
    Response.Write "Amount field is too long, please try again." 
Else 
   Set reg = New RegExp 
   reg.Pattern = "\D+" ' Look for something other than 0-9's. 
   if reg.Test(Request.QueryString("amount")) = False then 
      Values(0) = Clng(Request.QueryString("amount")) 
      '-------- AccessCheck ----------------------------------------- 
      Results = CCHandle.AccessCheck("Approve", 
                                     Scopes, Operations, Names, Values) 
      If Results(0) = 0 Then ' Zero = NO_ERROR 
         Response.Write "Approval of " & Values(0) & " was accepted." 
      Else 
         Response.Write "Approval of " & Values(0) & " was denied." 
      End If 
      '-------------------------------------------------------------- 
   Else 
      Response.Write "Must enter number, please try again" 
   End if 
End If 
Set CCHandle = Nothing 
set App = Nothing 
Set AzManStore = Nothing 
%> 
</BODY> 
</HTML> 

Option 2: Create custom COM objects and interfaces to control impersonation.

Create a custom COM object that allows you to control impersonation from your ASP application. After that, set up the IIS worker process to run in a service account as described above and use the custom COM object to call the RevertToSelf API to return to the IIS worker process context (the ASP applications service account). Then, call the ImpersonateSelf API to impersonate the context of the IIS worker process. (ASP expects to run applications under impersonation.) It is important to save the client token in this step because it will be used later.

By impersonating the IIS worker process from the custom COM object you can call Authorization Manager APIs to initialize the Authorization Manager policy store and the Authorization Manager application in the context of the ASP applications service account (the security context that you configured the worker process to use). This process of initializing the Authorization Manager policy store and application objects might only be done once when the ASP application initializes.

When a client request is made, you must create an Authorization Manager client context to use for checking the clients access. To do this, you can use the ASP server variable AUTH_USER to get the clients user name and call AzApplication.InitializeClientContextFromName. But you have the option of using your custom COM object to create a client context using the logon token that was created when IIS authenticated the client user. To do this, expose a function in your custom COM object to retrieve the token that you stored above and call the ImpersonateLoggedOnUser API. This will impersonate the client security context. Then, create an Authorization Manager client context using the AzApplication.InitializeClientContextFromToken method with (0,0) as parameters to create an Authorization Manager context from the current thread token. Since you are impersonating the client, the current thread token represents the client and the Authorization Manager client context created from this token will also represent the client. After doing this, use your custom COM object to re-impersonate the ASP application service account process again, as explained above.

This works because, when AzApplication.InitializeClientContextFromToken is called with the value of 0 for the token handle, the API creates an Authorization Manager context that is derived from the token of the calling thread. Since, in this case, we are impersonating the client user, the Authorization Manager context that is created represents the client.

Implementing a custom COM interface to manage impersonation requires additional work, but using a custom COM object to control impersonation has the benefit of using the clients token that is maintained in IIS to create a context as described. Methods that use the AzApplication.InitializeClientContextFromName interfaces result in querying the user account object in Active Directory. This can result in slower performance than when you use a token, because the token already has this information.

C, C++, and Visual Basic

You can control impersonation in native C, C++, or Visual Basic by calling various context management APIs, such as ImpersonateLoggedOnUser, OpenThreadToken, or RevertToSelf. In these languages, the application server can be installed as a Windows System service and run in a dedicated account for the service application. When the application initializes, it first calls AuthorizationStore.Initialize to connect and load information from the Authorization Manager policy store, and then AzApplication.Initialize to create a COM interface to the application policy within the policy store

When a user logs on to the application, it authenticates the client using whatever means it chooses. If a native Windows authentication mechanism is used, such as Security Service Provider Interface (SSPI), Distributed Component Object Model (DCOM), Remote Procedure Call (RPC), or Named Pipes, then the application server can use the resulting token to create an Authorization Manager context with one of two methods:

  • Impersonating the client by using the appropriate impersonation API, such as ImpersonateNamedPipeClient or ImpersonateSecurityContext in SSPI, and calling IAzApplication::InitializeClientContextFromToken, specifying 0 for the token handle value.

  • Querying the clients token handle, such as QueryContextAttributes in SSPI, and using that handle in the call to IAzApplication::InitializeClientContextFromToken.

If non-native Windows authentication is used, then applications can map the authenticated client to a Windows account and then use the IAzApplication::InitializeClientContextFromName API, specifying the Windows account name that the client was mapped to after authentication.