共用方式為


Using ADAM Principals in Authorization Manager

 

My name is Sudheer Mamidipaka. I am working in Windows Security Access Control team. I own testing of AzMan component.

 

We have lots of customers asking, if it’s possible to use AzMan to authorize ADAM principles. YES YOU CAN. But it just takes a little custom code. Here are some details and some sample code and scripts to do this:

 

Like Active Directory principals, ADAM principals can be assigned to groups (ADAM groups), and have credentials (username and password.) Unlike Active Directory principals, ADAM principals cannot logon to a Windows desktops or fileshares, or be authenticated through Windows Integrated Authentication. This means that applications that use ADAM principals need to authenticate the user credentials and query group memberships manually using LDAP interfaces. Typically applications authenticate ADAM principals using the ldap_bind API or a higher level wrapper such as Active Directory Services Interfaces (ADSI.) and query a users groups by querying the user’s tokenGroups attribute.

 

This is done by the addition of interfaces which allow applications to create an Authorization Manager empty context and then add the SIDs (user and group) to that context and then the ability to set the distinguished name of the ADAM principal for use by Authorization Manager dynamic ldap query groups. A custom management user interface is utilized to add the ADAM user and group SIDs to the role assignment.

There are two methods of authorizing ADAM principals with Authorization Manager.

The first approach checks for a SID match using the access check.  It is faster as it requires no searches. This approach is preferred in most development efforts due to speed.  It involves the following:

The applications that have authenticated an ADAM principal query the ADAM principal’s user and group SIDs in ADAM (done by using LDAP to query the principal’s tokenGroups attribute). The application adds the Sids to an Authorization Manager client context via the IAzClientContext2::AddSids interface. The application then provides the client context with the principal’s distinguished name (DN) by using the LDAPQueryDN attribute on the AzClientContext2 object.

The steps are as follows:

To create an ADAM group and assign a user to it:

Create ADAM Group (using ADAM ADSI Edit, LDP.EXE, another tool, or code)

Add ADAM principal to ADAM Group

In a custom Authorization Manager UI, create a Role Assignment and assign the ADAM user or group to a role (a custom UI is needed because the Windows Object Picker does not currently support ADAM.)

For testing purposes you could use the LDP.exe tool to retrieve the ADAM user or group sid and the Authorization Manager scriptable interfaces to assign the user or group to a role (such as the IAzRole::AddMember method.)

 

The application that uses Authorization Manager for application performs the following steps (see code sample below for detail):

Initialize the store and application (explained in previous section.)

  1. After a client connects and has been authenticated (typically via ldap_bind) create a client context using the IAzClientContext::InitializeClientContext2 interface
  2. Query the user’s objectSid attribute to obtain the user’s SID add this to the empty client context via the IAzCleintContext::AddStringSids method.

 

  1. Query the users tokenGroups attribute which will contain the user group SIDs (see sample code below.) Add ADAM group Sids to the client context object created above via the IAzCleintConetxt2::AddSids method.
  2. Query the client’s distinguished name in ADAM (see sample code below.)
  3. Add principal DN to client context via IAzClientContext2::LdapQueryDN which will support dynamic LDAP query groups

 

Here is the sample code for the above scenario. The code will

  • Authenticate ADAM user
  • Queries ADAM for ADAM user’s SID and groups SIDs
  • Initializes AzMan store and Application
  • Creates and empty ClientContext
  • Adds user and groups SIDs to the client context.
  • Sets User DN on the client context for Ldap queries
  • Calls an AccessCheck.

using System;

using System.Collections;

using System.Collections.Generic;

using System.DirectoryServices;

using System.Runtime.InteropServices;

using System.Text;

using Microsoft.Interop.Security.AzRoles;

namespace AzManADAMAuth

{

    class Program

    {

        static void Main(string[] args)

        {

            AuthenticationTypes AuthType = AuthenticationTypes.None;

            string UserDN, UserSid;

            ArrayList TokenGroupSids;

  if (args.GetLength(0) < 7)

            {

                Console.WriteLine("usage:\n \"AdamLogin\" \"ServerName\" \"Partition\" \"UserDN\" \"UserPassword\" \"AzManStoreURL\" \"AzManApplicationName\" \"OperationID\"");

                return;

   }

            try

            {

                LogonAdamUser(

                        args[0],

                        args[1],

                        AuthType,

                        args[2], args[3], out UserDN, out UserSid, out TokenGroupSids);

                Console.WriteLine("User Logged on Successfully:");

                Console.WriteLine("UserDN {0} , UserSid {1}", UserDN, UserSid);

               

                // Load AzMan Store

                AzAuthorizationStoreClass AzStore = null;

                AzStore = new AzAuthorizationStoreClass();

                AzStore.Initialize(0, args[4], 0);

                Console.WriteLine("Opened Store:");

                IAzApplication2 AzApp = null;

                AzApp = (IAzApplication2)AzStore.OpenApplication(args[5], null);

                Console.WriteLine("Opened Application:" + AzApp.Name);

                //Create Empty ClientContext

                IAzClientContext2 ClientCon = null;

                ClientCon = (IAzClientContext2)AzApp.InitializeClientContext2("Adam user", null);

                //Add user Sid and group sids to client context

                object[] userSids = new Object[TokenGroupSids.Count + 1]; //Group sids + user sid

               

                // Add UserSid

                userSids[0] = (object)UserSid;

                //AddGroup Sids

                int i = 1;

                foreach (string GroupSid in TokenGroupSids)

                {

                    userSids[i] = (object)GroupSid;

                    i++;

                }

                ClientCon.AddStringSids(userSids);

                Console.WriteLine("Added Adam user sid and group sids to client context.");

                //Set LDAP QueryDN for adam user. This is needed if LDAP query groups are involved.

                ClientCon.LDAPQueryDN = "LDAP://" + args[0] + "/" + UserDN;

             Console.WriteLine("Set LDAPQueryDN on ClientContext:" + ClientCon.LDAPQueryDN);

                //Do AccessCheck

                object[] scope = new Object[1];

                scope[0] = (object)""; //Application Scope

                object[] operations = new Object[1];

                operations[0] = Int32.Parse(args[6]);

                object[] results;

                results = (object[])ClientCon.AccessCheck("Adam User AccessCheck", (object)scope, (object)operations, null, null, null, null, null);

                foreach (int iRes in results)

                {

                    Console.WriteLine("*********************************");

                    if (iRes == 0)

                    {

                        Console.Out.WriteLine("ACCESS GRANTED");

                    }

                    else

                    {

                        Console.Out.WriteLine("ACCESS DENIED");

                    }

                    Console.WriteLine("*********************************");

              }

            }

            catch (Exception Ex)

            {

                Console.WriteLine(Ex.Message);

                Console.WriteLine(Ex.StackTrace);

            }

        }

        /****************************************************************

         *

         * Purpose: Given the userPrincipalName of a user and

         * it's password, retrieve the user's distinguishedName,

         * string sid, and tokenGroups.

         *

         ****************************************************************/

        public static void LogonAdamUser(

                string adamServer,

                string partitionName,

                AuthenticationTypes AuthType,

                string username,

                string password,

                out string userDN,

                out string userSid,

                out ArrayList tokenGroupSids

            )

      {

            string adsPath = null;

            if ((Environment.OSVersion.Version.Major < 5)

                || ((Environment.OSVersion.Version.Major == 5)

                    && (Environment.OSVersion.Version.Minor <= 1)

                   )

       )

            {

                adsPath = "LDAP://" + adamServer;

            }

            else

            {

                adsPath = "LDAP://" + adamServer + "/RootDSE";

            }

           

            //Incase you don't have SSL setup, change the AuthenticationTypes to AuthenticationTypes.None.

            DirectoryEntry entry = new DirectoryEntry(adsPath, username, password, AuthType);

            entry.RefreshCache(new string[] { "tokenGroups" });

            PropertyValueCollection propertyValues = entry.Properties["tokenGroups"];

            Console.WriteLine("Token groups = {0}", propertyValues.Count);

            tokenGroupSids = new ArrayList();

            foreach (object val in propertyValues)

            {

                string stringSid = ConvertSidToStringSid((byte[])val);

                tokenGroupSids.Add(stringSid);

            }

            adsPath = "LDAP://" + adamServer + "/" + partitionName;

            entry.Path = adsPath;

            string filter = "(&(objectClass=user)(userPrincipalName=" + username + "))";

            string[] propertiesToLoad = new string[] { "objectSid", "distinguishedName" };

            DirectorySearcher searcher = new DirectorySearcher(entry, filter, propertiesToLoad, SearchScope.Subtree);

            // UPN has to be unique for authentication to work.

            // So assuming that only 1 entry will be returned.

            SearchResult result = searcher.FindOne();

            if ((result.Properties.Contains("distinguishedName")) && (result.Properties["distinguishedName"].Count > 0))

                userDN = result.Properties["distinguishedName"][0].ToString();

            else

                userDN = null;

            if ((result.Properties.Contains("objectSid")) && (result.Properties["objectSid"].Count > 0))

                userSid = ConvertSidToStringSid(((byte[])(result.Properties["objectSid"][0])));

            else

                userSid = null;

        }

        [DllImport("Advapi32.dll", EntryPoint = "ConvertSidToStringSidW", CharSet = CharSet.Unicode, SetLastError = true)]

        public static extern int ConvertSidToStringSidW(IntPtr pSid, ref IntPtr stringSid);

        [DllImport("kernel32.dll", EntryPoint = "LocalFree")]

        public static extern int LocalFree(IntPtr mem);

        /**********************************************************

         *

         * Purpose: To convert the sid in byte form to string form.

         *

         **********************************************************/

        private static string ConvertSidToStringSid(byte[] sidBytes)

        {

            string stringSid = null;

            IntPtr ptr = (IntPtr)0;

            // Allocate memory for Byte[]

            IntPtr sidPtr = Marshal.AllocHGlobal(sidBytes.Length);

           // Copy byte[] to allocated memory

            Marshal.Copy(sidBytes, 0, sidPtr, sidBytes.Length);

            //Convert sid to string sid

            int result = ConvertSidToStringSidW(sidPtr, ref ptr);

            // Free allocated memory

            Marshal.FreeHGlobal(sidPtr);

            if (result == 0)

            {

                Console.WriteLine("ERROR converting sid to string sid: {0}", Marshal.GetLastWin32Error());

            }

            else

            {

                try

            {

                    stringSid = Marshal.PtrToStringUni(ptr);

                }

                finally

                {

                    LocalFree(ptr);

                }

            }

            return stringSid;

        }

    }

}

 

usage:

AzManADAMAuth "<ADAMServername:Port>" "<PartitionName>" "<UserName>" "<Password>" "<AzManStoreURL>" "<AzManApplicationName>" "<AzManOperationID>"

Following VB script is to add a SID to a Role in AzMan store. Script can be extended to add the SID to any Role\Group in Application\Scope of AzMan store.

 

'Script to Add a SID to Role in AzMan Aapplication

'Can be extended to add the sid to Role/Group in Store/Application/scope

'Usage AddSidToAzMan "StoreURL" "ApplicationName" "RoleName" "SID"

Dim AzSt

Set AzSt = CreateObject("AzRoles.AzAuthorizationStore")

AzSt.Initialize 0, WScript.Arguments(0)

Dim AzApp

Set AzApp = AzSt.OpenApplication(WScript.Arguments(1))

WSCript.Echo "Opened Appplicaton:" & AzApp.Name

Dim AzRole

Set AzRole = AzApp.OpenRoleAssignment(WScript.Arguments(2))

Wscript.Echo "Opened RoleAssignment:" & AzRole.Name

AzRole.AddMember WScript.Arguments(3), 0

AzRole.Submit 0,0

WScript.Echo "Added SID:" & WScript.Arguments(3) & " to Role: " & WScript.Arguments(2)

 

Thanks to ADAM team for providing sample code of ADAM user logon.

 

Thanks,

Sudheer Mamidipaka

Comments

  • Anonymous
    May 06, 2006
    Check out this really cool post from the AzMan Team blog! http://blogs.msdn.com/azman/archive/2006/05/06/591230.aspx&amp;nbsp;...
  • Anonymous
    June 14, 2006
    Can anyone confirm weather or not AzMan can be run on a Windows XP SP2 PC? I have successfully setup ADAM and have user authentication working correctly. I know would like to store application specific roles in AzMan. I a vae been using the following two links for references to set this up.

    1. How To: Use ADAM for Roles in ASP.NET 2.0
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/PAGHT000018.asp
    2. How To: Use Authorization Manager (AzMan) with ASP.NET 2.0
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/paght000019.asp

    I have downloaded and installed both the following items on my Windows XP SP2 machine:
    Windows Server 2003 Administration Tools Pack SP1 and
    Windows 2000 Authorization Manager Runtime and have primary interop assembly for the AzMan COM object into the global assembly cache in both the .Net 1.1 and 2.0 GAC.

    The problem seems to occur when I attempt to create a new Authorization Store. I am in developer mode of the azman.msc and choose Create New Authorization Store -> Active Directory. When I enter the following for the store name I continue to get errors and the store is not created.

    msldap://localhost:389/CN=AuthorizationStore,o=MyOrganization

    The error is:
    Cannot Create a New Authorization Store
    The following problem occured: Unable to Update the Password
    The value provided as the current password is incorrect.

    Anybody have any ideas? Thanks for any help!
  • Anonymous
    June 14, 2006
    The comment has been removed
  • Anonymous
    November 22, 2006
    In order to add an ADAM principal to an AzMan role, one could use the ADSI Edit itself (I think), if the AzMan store is in ADAM. Locate the AzMan store in ADAM and the associated AzRoleObjectContainer. Select the Role and in the Properties window select msDS-MembersForAzRole. This allows you to add an ADAM Account to the AzMan Role without having to make use of a custom UI that processes ADAM SIDs
  • Anonymous
    June 06, 2007
    Thanks for posting this detailed infromatoin which help us to use ADAM.AZMan combination for our application. how ever i need to provide one user interface thru application which can allow me to add ADAM users to AZMan Role. can you give some guidelines for doing this.
  • Anonymous
    June 07, 2007
    Currently this requires a custom UI.  Custom UIs are somewhat common for AzMan as many apps want to integrate the authorization management experience with their other policy / config management. To create a custom UI you use the AzMan administration interfaces. There is a sample in the Windows SDK that shows how to use these (called AzMigrate.) It is not a UI sample, but uses the AzMan admin API to demo a tool that migrates a store from one place to another.Download the SDK at:http://www.microsoft.com/downloads/details.aspx?familyid=7614FE22-8A64-4DFB-AA0C-DB53035F40A0&displaylang=enMake sure the samples are installed. The AzMigrate sample is in the following folder:<sdk folder>samplessecurityauthorizationazman(Note in Vista and Windows Server 2008 the AzMan MMC UI has an extensibility point to allow you to just write a custom ADAM picker and leverage the rest of the MMC UI. For a demo of this see the above post about the Keith Brown video and check out the 4th video.)HTH-Dave
  • Anonymous
    January 18, 2009
    PingBack from http://www.keyongtech.com/2862149-customer-authentication-center
  • Anonymous
    January 21, 2009
    PingBack from http://www.keyongtech.com/2855170-azman-adam-accesscheck-exception