Active directory authentication - asp.net

I'm using the code below to authenticate a user in Active Directory, but the password is sending in clear text. How can I hash my password and then send it to Active Directory?
DirectoryEntry entry = new DirectoryEntry(path, username, pwd);
try
{
//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;
}
//Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (string)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;

If you are using .NET 3.5, then I'd strongly recommend switching to using the System.DirectoryServices.AccountManagement namespace (read all about it: Managing Directory Security Principals in the .NET Framework 3.5).
Lots of things are a lot easier in S.DS.AM - like authenticating users:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
ctx.ValidateCredentials("test", "test", ContextOptions.SecureSocketLayer);
The only way to do this securely is by specifying the ContextOptions.SecureSocketLayer option to enforce using an SSL protected connection.
If you cannot move to .NET 3.5 and S.DS.AM, you need to check out the AuthenticationTypes that you can define in the fourth overloaded constructor of DirectoryEntry:
DirectoryEntry entry =
new DirectoryEntry(path, username, pwd,
AuthenticationTypes.SecureSocketsLayer);
There's no other way to do this, I'm afraid - I don't think there's any way for you on the client-side to hash a password the same way Windwos Server / Active Directory do it, and pass in that hashed value...

Related

WEB API + Cache all users from AD on application_start

I have a WEB API which perform 2 functions
return current login user details
Also enable asyn search for any user.
In order to save performance hit on first call, i want to get all users and save them in cache on application_start. I have enabled IIS warm up, so everything should be in cache before first request
DirectoryEntry entry = new DirectoryEntry(LdapPath, activeDirectoryUserid, activeDirectoryPassword);
DirectorySearcher search = new DirectorySearcher(entry)
{
Filter = "(&(&(objectClass = user)(objectClass = person)))"
};
search.PropertiesToLoad.Add(AppConstants.AD.Name);
search.PropertiesToLoad.Add(AppConstants.AD.SamAccountName);
search.PropertiesToLoad.Add(AppConstants.AD.ThumbnailPhoto);
search.PropertiesToLoad.Add(AppConstants.AD.FirstName);
search.PropertiesToLoad.Add(AppConstants.AD.Manager);
search.PropertiesToLoad.Add(AppConstants.AD.DistinguishedName);
search.CacheResults = false;
SearchResultCollection resultset = search.FindAll();
Try using System.DirectoryServices.AccountManagement namespace.
using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "yourdomain.com"))
{
UserPrincipal allUsers = new UserPrincipal(ctx);
PrincipalSearcher ps = new PrincipalSearcher(allUsers);
foreach (UserPrincipal user in ps.FindAll())
{
//USER will contain the necessary information you need
string name = user.DisplayName;
}
}

Unknown username or bad password, LDAP Active Directory

I'm trying to authenticate against AD using application mode (ADAM), but keep getting unknown username or bad password. If I test the login in LDP.exe it logs in no problem, on simple bind. I've trawled through all similar posts with the same issue, but have not resolved it, any suggestions what I should be checking for?
private bool ValidateActiveDirectoryLogin(string Username, string Password)
{
bool Success = false;
System.DirectoryServices.DirectoryEntry Entry = new System.DirectoryServices.DirectoryEntry("LDAP://localhost:389/OU=Users,O=TestDirectory", Username, Password);
System.DirectoryServices.DirectorySearcher Searcher = new System.DirectoryServices.DirectorySearcher(Entry);
Searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree;
try
{
System.DirectoryServices.SearchResult Results = Searcher.FindOne();
Success = (Results != null);
}
catch (Exception ex)
{
Success = false;
throw;
}
return Success;
}
Determine what context your application is hitting AD with. If your ASP.NET application pool identity is one that is low privileged, it won't have enough permissions to query active directory. If you don't want to create a custom user to run the app pool as with appropriate permissions - you could use the LogonUser API to make your ValidateActiveDirectoryLogin call under the security context of that account.
Finally, you should consider using System.DirectoryServices.AccountManagement if you are using .NET 3.5 or above.
You can use code like
bool validCreds = false;
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
validCreds = context.ValidateCredentials( username, password );
}

System.DirectoryServices - The server is not operational

I get an error by a website, on which I use Windows Authentication.
Strange things:
Only occurs if user is not yet saved into database (new unknown user)
Appears only on live system, everything fine on local development environment
This is what I get in a logging mail:
Source : System.DirectoryServices
Message: The server is not operational.
Trace:
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
at Smarthouse.Labs.DataAccess.UserListManager.SaveUser(String windowsUserName)
This is how I implement DirectorySearch:
private void SaveUser(string windowsUserName)
{
string[] domainAndUser = windowsUserName.Split('\\');
string domain = domainAndUser[0];
string username = domainAndUser[1];
DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain);
DirectorySearcher search = new DirectorySearcher(entry);
try
{
// Bind to the native AdsObject to force authentication.
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
search.PropertiesToLoad.Add("sn");
search.PropertiesToLoad.Add("givenName");
search.PropertiesToLoad.Add("mail");
SearchResult result = search.FindOne();
if (result == null)
{
throw new Exception("No results found in Windows authentication.");
}
User userToSave = new User();
userToSave.FirstName = (String) result.Properties["givenName"][0];
userToSave.LastName = (String) result.Properties["sn"][0];
userToSave.Email = (String) result.Properties["mail"][0];
userToSave.Username = windowsUserName;
userToSave.Guid = Guid.NewGuid();
SaveUser(userToSave);
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message, ex);
}
finally
{
//Dispose service and search to prevent leek in memory
entry.Dispose();
search.Dispose();
}
}
If more code examples are needed just tell me.
Your problem is that you're using a "plain" domain name to bind - this won't work in LDAP. Actually, if you try to bind to LDAP://MyDomain, what you're really doing is trying to bind to the server called MyDomain.
You need a valid LDAP bind string - something like LDAP://dc=yourdomain,dc=local or something.
To find out what your default LDAP binding context is, use this code snippet:
DirectoryEntry deRoot = new DirectoryEntry("LDAP://RootDSE");
if (deRoot != null)
{
string defaultNamingContext = deRoot.Properties["defaultNamingContext"].Value.ToString();
}
Once you have that string - use that as your bind string to your LDAP server.
And if you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context -- no domain name needed, uses default domain
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username);
if(user != null)
{
// do something here....
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
You can use bind strings in the format LDAP://mydomain.com:389. I kept getting "Access is Denied" when trying to use the format LDAP://DC=mydomain,DC=com. Once I switched to the LDAP://mydomain.com:389 format, and bound using the AuthenticationTypes.ServerBind flag when constructing my DirectoryEntry, it worked great. This was in Azure App Service.
To add to marc_s's answer above, I needed to search multiple domains.
So for each Domain I did the following:
DirectoryEntry deRoot = new DirectoryEntry("LDAP://" +"DomainName"+ "/RootDSE");
string defaultNamingContext = "LDAP://" + deRoot.Properties["defaultNamingContext"].Value.ToString();
DirectoryEntry mySearchRoot = new DirectoryEntry(defaultNamingContext);
DirectorySearcher myDirectorySearcher = new DirectorySearcher(mySearchRoot);
Similar Error Happened to me (though it happened all the time and not in specific cases like pointed out here) because of a wrong Active Directory connection string. i used the corp instead the prod one .
Use something that works for another app in your organization if exists.

How to get user details in asp.net Windows Authentication

I am using windows Authentication and accessing user name as.
IIdentity winId = HttpContext.Current.User.Identity;
string name = winId.Name;
but i want to get other details like User full name and EmailID.
Since you're on a windows network, then you need to query the Active directory to search for user and then get it's properties such as the email
Here is an example function DisplayUser that given an IIdentity on a windows authenticated network, finds the user's email:
public static void Main() {
DisplayUser(WindowsIdentity.GetCurrent());
Console.ReadKey();
}
public static void DisplayUser(IIdentity id) {
WindowsIdentity winId = id as WindowsIdentity;
if (id == null) {
Console.WriteLine("Identity is not a windows identity");
return;
}
string userInQuestion = winId.Name.Split('\\')[1];
string myDomain = winId.Name.Split('\\')[0]; // this is the domain that the user is in
// the account that this program runs in should be authenticated in there
DirectoryEntry entry = new DirectoryEntry("LDAP://" + myDomain);
DirectorySearcher adSearcher = new DirectorySearcher(entry);
adSearcher.SearchScope = SearchScope.Subtree;
adSearcher.Filter = "(&(objectClass=user)(samaccountname=" + userInQuestion + "))";
SearchResult userObject = adSearcher.FindOne();
if (userObject != null) {
string[] props = new string[] { "title", "mail" };
foreach (string prop in props) {
Console.WriteLine("{0} : {1}", prop, userObject.Properties[prop][0]);
}
}
}
gives this:
Edit: If you get 'bad user/password errors'
The account that the code runs under must have access the users domain. If you run code in asp.net then the web application must be run under an application pool with credentials with domain access. See here for more information
You can define a MyCustomIdentity by overriding from IIdentity and add your own properties etc.
Cast it to the specific Identity, for example WindowsIdentity

Active Directory Account locking out on first try

I have a website which requires users to enter their corporate network username and password. It then looks for that account in Active Directory and gets a list of any email addresses associated with that account.
The problem I am having is that ONE incorrect password is locking out an account. Our domain policy is that an account will lock out after three incorrect entries, so I am assuming that I am doing something wrong in my code. I am not very knowledgeable about Active Directory or .NET DirectoryServices in general, which may be apparent from my code. Here it is:
public ArrayList AuthenticateActiveDirectory(string Domain, string UserName, string Password)
{
// An error occurs if the username/password combo does not exist.
// That is how we know it is not a valid entry.
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + Domain, UserName, Password);
object nativeObject = entry.NativeObject;
ArrayList emails = new ArrayList();
DirectorySearcher ds = new DirectorySearcher(entry);
ds.Filter = "samaccountname=" + UserName;
ds.PropertiesToLoad.Add("mail");
SearchResult sr = ds.FindOne();
if (sr.Properties["mail"] != null)
{
for (int email = 0; email < sr.Properties["mail"].Count; email++)
{
emails.Add(sr.Properties["mail"][email]);
}
}
return emails;
}
catch (DirectoryServicesCOMException) { throw; }
catch (Exception) { throw; }
}
I did some searching and found some code (thanks to Ayende Rahien for the solution) to use that just authenticates and doesn't search for emails or anything else. I am using this prior to the other function, and it seems to be working fine. I am guessing that my other code is hitting AD more than once - at least 3 times - which is resulting in the lockout. Here is the code I am using now to just authenticate:
private bool Authenticate(string domain, string user, string password)
{
try
{
using (DirectoryEntry de = new DirectoryEntry("LDAP://" + domain,
user, password))
{
return de.NativeObject != null;
}
}
catch
{
return false;
}
}

Resources