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.)
- After a client connects and has been authenticated (typically via ldap_bind) create a client context using the IAzClientContext::InitializeClientContext2 interface
- Query the user’s objectSid attribute to obtain the user’s SID add this to the empty client context via the IAzCleintContext::AddStringSids method.
- 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.
- Query the client’s distinguished name in ADAM (see sample code below.)
- 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&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