Saturday, September 10, 2005

Accessing LDAP from ASP.NET

This article will not only guide you how to access LDAP from asp.net application but it will also try to shed light on some of the issues you will face while accessing LDAP from web application. We have to take care of some security issues while accessing LDAP from asp.net. For winform application it’s not problematic but in web application we have to take into account impersonation also. My class contain two member variable

private string _path;
private string _filterAttribute

Construtor of this will form path string which will be passed to while making DirectoryEntry object. Below is the code of constructor

public LdapAuthentication(string domain)
{
_path = "GC://";
string[] aSubdomain = domain.Split('.');
foreach (string strDomain in aSubdomain)
{
_path += "DC=" + strDomain + ",";
}
_path = _path.Substring(0, _path.Length-1);
}
This will produce path of following format
_path = "LDAP://DC=HO,DC=ABC-COMPANY,DC=COM"
Now the gist of whole class is IsAuthenticated method which is
///
/// Check if the provided information is authenticated or not
///

/// Domain
/// user name to be verified
/// password corresponding to the user name
/// true in case of authentication else false
public bool IsAuthenticated(string domain, string username, string pwd)
{
string domainAndUsername = domain + @"\" + username;
DirectoryEntry entry = new DirectoryEntry( _path, domainAndUsername, pwd);
// Bind to the native AdsObject to force authentication.
Object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if(null == result)
{
return false;
}
else
{
// Update the new path to the user in the directory
_path = result.Path;
_filterAttribute = (string)result.Properties["cn"][0];
return true;
}
}

Previously I was using same function but then I came across some article which pointed out that FindOne method has memory leak under certain condition for example, it will produce memory leak if it wont find any result, therefore I would recommend you to replace findOne with findAll which is not buggy. Now the method will be like
SearchResult result = null; using (SearchResultCollection src = search.FindAll()){ if (src.Count > 0) result = src[0];}
if (result != null){ // Update the new path to the user in the directory
_path = result.Path;
_filterAttribute = (string)result.Properties["cn"][0];
return true;
}
else
{
return false;
}

This method will update the path string in case of success of authentication. Now the string will be like
_path = "GC://CN=SAJWANI Rameez,OU=Users,OU=DUBAI,DC=ho,DC=cma-cgm,DC=com"
We will also store cn property from LDAP which will be helpful in retrieving other information. Like email , last name etc.
To find out FirstName we will use following method

public string GetFirstName()
{
DirectoryEntry entry = new DirectoryEntry( _path);
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(cn=" + _filterAttribute + ")";
search.PropertiesToLoad.Add("givenName");
try
{
SearchResult result = null; using (SearchResultCollection src = search.FindAll()){ if (src.Count > 0) result = src[0];}
if (result != null){ // Update the new path to the user in the directory
return result.Properties["givenName"][0].ToString();
}
}
catch
{
return "";
}
}

Similarly to get email address we will use following:
return result.Properties["mail"][0].ToString();
In this way we can load any property we want. Now the real thing come to run this code under asp.net. One thing to remember is that we are connecting to LDAP by providing username and password we can also connect under the security context of the ASP.NET Web user.
Before addressing the security issue we wil first look at how LDAP actually works.

The Active Directory (AD) relies on the security mechanism of the Windows 2000 server. To access most information in the AD, you must provide credentials to the Windows 2000 server when requesting the AD information. The credentials you provide must be in a primary token, which just means that the IIS server has a password (not just a hash of the password) to pass to the AD.

Double-Hop Issue
The double-hop issue is when the ASPX page tries to use resources that are located on a server that is different from the IIS server. In our case, the first "hop" is from the web browser client to the IIS ASPX page; the second hop is to the AD. The AD requires a primary token. Therefore, the IIS server must know the password for the client to pass a primary token to the AD. If the IIS server has a secondary token, the NTAUTHORITY\ANONYMOUS account credentials are used. This account is not a domain account and has very limited access to the AD. The double-hop using a secondary token occurs, for example, when the browser client is authenticated to the IIS ASPX page by using NTLM authentication. In this example, the IIS server has a hashed version of the password as a result of using NTLM. If IIS turns around and passes the credentials to the AD, IIS is passing a hashed password. The AD cannot verify the password and, instead, authenticates by using the NTAUTHORITY\ANONYMOUS LOGON. On the other hand, if your browser client is authenticated to the IIS ASPX page by using Basic authentication, the IIS server has the client password and can make a primary token to pass to the AD. The AD can verify the password and does authenticate as the domain user.
How to acquire Primary Token
If the IIS server has a primary token to pass on, the IIS server can pass a primary token to the AD on behalf of the client requesting the ASPX page. To acquire a primary token by using ASPX, use one of the following methods.

Method A
When the Web.config file is set to identity impersonate="true"/ and authentication mode="Windows", use the Anonymous account with the following settings:
On the ASPX page, set the security mechanism to Anonymous only.
Clear the Allow IIS to control the password check box.
Set the Anonymous account to be a domain user.

Method B
When Web.config and Machine.config are set as follows:
When Web.config is set to identity impersonate="false"/ and authentication mode="Windows"
When Machine.config is set to processModel username=Domain\username,password=secret
If identity impersonate="false"/ in the Web.config file, the credentials of the Base process are used. When you supply a domain user and password, you make it possible for IIS to pass a primary token to the AD.

ASP.NET Base Account
By default, all ASP.NET applications run under the base process account, MACHINENAME\ASPNET. This is a local account that does not have access to objects in Active Directory. To access Active Directory by using the credentials that are passed to IIS, you must modify your Web.config file to contain the parameters identity impersonate="true" and authentication mode="Windows". The presence of these two parameters causes ASP.NET to run the code under the credentials that are passed to it by IIS.
I case of my scenario I was using Form Base Authentication. All the user of my application are my intranet so for every request I was putting the principle object of that user in context. By using following line.

// This principal will flow throughout the request.
GenericPrincipal principal = new GenericPrincipal(id, null);
// Attach the new principal object to the current HttpContext object
Context.User = principal;

Some time LDAP connection works from local host but i wont if i will be access from remot machine in that case make sure that integrated login is working.

Note : In order to make sure the Integrated Login works with AD you need to two specials things Number
1: Go the domain controller of your webserver. Go to the Computers Container and Find your webserver. Go in properties and check the allow delegation option. You may have to restart the computer.
- Numebr 2 Make sure the IntegratedLogin is enabled on the InternetExplorer. You will find this option in the Tools --> Internet Options --> Advanced option


Reference.
http://support.microsoft.com/default.aspx?scid=kb;en-us;329986
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sds/sds/active_directory_authentication_from_asp__net.asp

7 Comments:

At 4:55 AM, Blogger Ali said...

Need some help with elusive connection string. So far, I have connected to ldap using 3rd party tool ok and am able to bind using my credentials. I have modified my web.config file to impersonate to address the double hop issue. So I am pretty sure my connection string is not correct. I don't understand why my string needs to use samaccountname? Is that stored in AD? code is follows

Dim domainAndUsername As String = domain & "\" & username

Dim entry As DirectoryEntry = New DirectoryEntry(_path, domainAndUsername, pwd, AuthenticationTypes.Secure)

Dim myuser As Object = System.Security.Principal.WindowsIdentity.GetCurrent().Name()
Dim obj As Object = entry.NativeObject

ldap path is as per 3rd party DN setting to be

Dim adPath as String = "LDAP://mail.aus.edu:389/uid=SElement,ou=people,ou=Staff4,ou=Employee,o=aus.edu,o=ausedu"

 
At 6:09 AM, Blogger Prashant Kurapati said...

can add users successfully but now I want to verify the these user against LDAP.







Here connection string is a root path.



DirectoryEntry de = new DirectoryEntry(_ConnectionInfo.ConnectionString, user.DistinguishedName, Password, (AuthenticationTypes)_ConnectionInfo.AuthenticationType);



But it throws an exception saying that “user name or password is bad”

 
At 11:02 PM, Blogger RaviKumar said...

Hi i want to crate a local LDAP server. Can any body help me on this how to create a local LDAP Server

 
At 11:02 PM, Blogger RaviKumar said...

Hi i want to crate a local LDAP server. Can any body help me on this how to create a local LDAP Server

 
At 10:05 PM, Blogger Farheen said...

Hello,
I had implemented the concept of LDAP , but the i was getting only those PC on the Domain to which my machine is connected ... i used following code...Please help me out to find the system available in other domain...


Dim entry As DirectoryEntry
entry = New DirectoryEntry("LDAP://" & strDomainName, UserName, Password)

entry.AuthenticationType = AuthenticationTypes.Secure

Dim mySearcher As DirectorySearcher
mySearcher = New DirectorySearcher(entry)

mySearcher.Filter = ("(objectClass=computer)")

Dim res_collection As SearchResultCollection
res_collection = mySearcher.FindAll()
intTotalAsset = res_collection.Count

 
At 7:44 PM, Blogger dhd said...

welcome to the wow power leveling, cheap service site, buy cheap wow gold,wow gold,world of warcraft power leveling buy wow power leveling

 
At 10:47 PM, Blogger wdmtest said...

Using PLNQ queries
Managing deferred query execution
Controlling concurrency
Handling PLINQ Exceptions
Cancelling PLINQ queries
Setting merge options
Using custom partitioning
Using custom aggregation
Generating parallel ranges
Common problems and their causes
Testing & debugging
Making things better when everything goes wrong
Measuring parallel performance
Finding parallel bugs
Common parallel algorithms
Sorting, searching, and caching
Using parallel map and reductions
Speculative processing
Using producers and consumers

 

Post a Comment

<< Home