to retrieve windows login-password via asp.net - c#

public Object IsAuthenticated()
{
String domainAndUsername = strDomain + "\\" + strUser;
***DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, strPass);***
SearchResult result;
try
{
//Bind to the native AdsObject to force authentication.
DirectorySearcher search = new DirectorySearcher(entry) { Filter = ("(SAMAccountName=" + strUser + ")") };
search.PropertiesToLoad.Add("givenName"); // First Name
search.PropertiesToLoad.Add("sn"); // Last Name
search.PropertiesToLoad.Add("cn"); // Last Name
result = search.FindOne();
if (null == result)
{
return null;
}
//Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (String)result.Properties["cn"][0];
}
catch (Exception ex)
{
return new Exception("Error authenticating user. " + ex.Message);
}
return user;
}
In the above code segment, is there a way to retrieve the user's Windows login password so that the LDAP authentication works without asking the user his password another time?
Can the value for "strPass",that is being passed when DirectoryEntry object is being created, be retrieved by any way?

The password does not exist anywhere. It would be a big security hole if it did.
Also, BTW, get rid of the try/catch block. It's doing nothing but hiding the reason for the exception.

Use the ActiveDirectoryMemebershipProvider - You can authenticate without writing code, thus eliminating the login scenario you currently have.

You could set up Windows Authentication in your ASP.NET app.
http://msdn.microsoft.com/en-us/library/ff647405.aspx
Once you set this up, only authenticated users have access to the protected parts of your site.
This gives you access some key bits of information.
For example:
System.Web.HttpContext.Current.User.Identity.Name - gives the name (domain\username) of an authenticated user.
System.Web.HttpContext.Current.User.IsInRole("role_name_here") - will tell you if the authenticated user is in a given role.
Authentication can be tricky to do for the first time - please do not ask a user for their windows password - this is a security risk - allow IIS and the .NET framework take care of this for you. The article above may be a bit long and seem a bit complicated but it has a lot of good information in it.

Related

DirectorySearcher.ClientTimeout - Search timed out or really 0 results?

I maintain a C# program which needs to check whether thousands of Active Directory accounts are still in existence & if they are enabled or not. Recently, I've found that my program was hanging while querying the directory for an account. Fortunately, I've just discovered the DirectorySearcher.ClientTimeout property (which I hadn't been setting, meaning that the search goes on indefinitely).
The one problem that I see with using this property is that, if the search hangs while looking up an account that happens to actually exist, the DirectorySearcher.FindOne() method will return 0 results. As you can imagine, that's a problem since at runtime, I don't know whether the search failed or if the account really wasn't found.
Does anyone know if there's another property that gets set in the object that I can use to see if the search aborted? Is there any difference between a result set from an aborted search versus one that really contains zero results?
Here's my method:
public static string UserExists(string username, Log log)
{
string accountStatus;
if (username.Split('\\').Length != 2)
return "Invalid ID,Invalid ID";
else
{
try
{
string[] parts = username.Split('\\');
domain = parts[0];
ScopeDN = "DC=" + domain + ",DC=contoso,DC=com";
DirectoryEntry de = GetDirectoryEntry("LDAP://" + ScopeDN);
DirectorySearcher ds = new DirectorySearcher();
ds.SearchRoot = de;
ds.ClientTimeout = TimeSpan.FromSeconds(30);
ds.Filter = "(&(objectClass=user) (sAMAccountName=" + username + "))";
SearchResult result = ds.FindOne();
if (result == null)
accountStatus = "Does Not Exist,Account Does Not Exist";
else
{
int UAC = (int)result.Properties["userAccountControl"][0];
bool enabled = !Convert.ToBoolean(UAC & 0x00002);
if (enabled)
accountStatus = "Exists,Account is Enabled";
else
accountStatus = "Exists,Account is Disabled";
}
return accountStatus;
}
catch (Exception e)
{
log.exception(LogLevel._ERROR, e, false);
return "Exception,Exception";
}
}
}
Try doing the following optimizations:
Dispose DirectoryEntry
Dispose DirectorySearcher
Create and cache a connection to RootDse object before querying users. In this case all AD queries will use one single cached connection to AD, which significantly increases performance.
For example
var entry = new DirectoryEntry("LDAP://contoso.com/RootDSE");
entry.RefreshCache();
// making user search and other AD work with domain contoso.com here
entry.Dispose();
AFAIK, there is no difference. One thing you could do is retry a couple times before assuming it is missing. Its a matter of confidence. One failure may not give high enough confidence. But 2 or 3 might. Does that help you?

Adding User to AD Security Group fails after user creation

I am using a WCF service to expose certain Active Directory management functions to our help desk staff without giving them the group membership required to manipulate AD directly. Adding users to and removing users from groups is working like a champ with existing users, but every time I create a new user it throws back this fun code:
The server is unwilling to process the request. (Exception from HRESULT: 0x80072035)
The code I use to add the user to the group is
public bool AddGroupToUser(string userDn, string groupDn)
{
try
{
DirectoryEntry groupEntry = LdapTools.GetDirectoryEntry(groupDn);
groupEntry.Properties["member"].Add(userDn);
groupEntry.CommitChanges();
groupEntry.Close();
return true;
}
catch (DirectoryServicesCOMException)
{
return false;
}
}
Everything I've read on the subject is rather vague and I can't seem to find out WHY the exception is being triggered. Any ideas?
UPDATE
This is the code I use to create the user in AD:
try
{
DirectoryEntry container = GetDirectoryEntry(storageOu);
DirectoryEntry newUser = container.Children.Add("CN=" + employee.FullName, "user");
newUser.Properties["sAMAccountName"].Value = employee.Username;
newUser.Properties["displayName"].Value = employee.FullName;
newUser.Properties["givenName"].Value = employee.FirstName;
newUser.Properties["sn"].Value = employee.LastName;
newUser.Properties["department"].Value = departmentName;
newUser.Properties["userPrincipalName"].Value = employee.Username + "#APEX.Local";
newUser.CommitChanges();
newUser.Invoke("SetPassword", new object[] { employee.Password });
newUser.CommitChanges();
AdsUserFlags userSettings = AdsUserFlags.NormalAccount;
newUser.Properties["userAccountControl"].Value = userSettings;
newUser.CommitChanges();
ldapPath = newUser.Path;
newUser.Close();
container.Close();
}
catch (DirectoryServicesCOMException e)
{
// Something went wrong... what???
}
catch (Exception e)
{
// Something else went wrong
}
The new user can login, and can be manipulated using the standard MS tools.
Apparently, unless I'm missing an important step here, the issue is time. When forcing the system to sleep for 8 seconds before attempting to add groups to the new user the process works. If I do it any sooner than the 8 second mark it fails.
I'm marking this answer as correct unless anybody can provide me a better solution.
Try:
public bool AddUserToGroup(string userName, string groupName)
{
bool done = false;
GroupPrincipal group = GroupPrincipal.FindByIdentity(context, groupName);
if (group == null)
{
group = new GroupPrincipal(context, groupName);
}
UserPrincipal user = UserPrincipal.FindByIdentity(context, userName);
if (user != null & group != null)
{
group.Members.Add(user);
group.Save();
done = (user.IsMemberOf(group));
}
return done;
}
Reference:
http://www.c-sharpcorner.com/UploadFile/dhananjaycoder/activedirectoryoperations11132009113015AM/activedirectoryoperations.aspx
As it mentioned in here, can you tell us that you set the password of the newly created users? In the reference, it says that you should SetPassword of the user before doing anything with it.
Time issue could occur from a Active Directory replication issue.
Once, I gave a product to my customer which creates user over Active Directory, which has over 10.000 records in it, with the given informations on a SharePoint form and then program adds the user to a SharePoint group. The problem was, SharePoint throws an error about the newly created user, it says user does not exist in AD.
So, one of out system engineer told us about the replication opearation on the Active Directory that it could be the source of the problem and it was true.
For the solution, program tries to do the job for 10 times with 1 second sleeps. No problem occurred so far. So as a solution, I would suggest you to check out the AD for the existence of replication method.
P.S: When I asked questions to my customers system engineers about the replication operation, they all rejected the existence of AD replication operation and told me that the program has the problem. They believed me when we created a new user in AD from a computer, we couldn't see the user for 5 seconds on an another computer.

Creating Active Directory user with password in C#

I'm looking for a way to create Active Directory users and set their password, preferably without giving my application/service Domain Admin privileges.
I've tried the following:
DirectoryEntry newUser = _directoryEntry.Children.Add("CN=" + fullname, USER);
newUser.Properties["samAccountName"].Value = username;
newUser.Properties["userPassword"].Value = password;
newUser.Properties["mail"].Value = email;
newUser.CommitChanges();
The user is created, but it seems the password is never set on the user.
Does anyone have an idea on how to set the user's password initially when creating the user? I know about
.Invoke("SetPassword", new object[] { password })
But that requires my code to be run with Domain Admin privileges. As I don't really see the point to grant my code Domain Admin privileges, just to set the initial password (I also allow user password resets, but those run in the context of that particular user), I am hoping someone has a clever solution that doesn't require me to do so.
Thanks in advance!
You can do this whole process much easier now with System.DirectoryServices.AccountManagement (long as you're on .Net 3.5):
See here for a full rundown
Here's a quick example of your specific case:
using(var pc = new PrincipalContext(ContextType.Domain))
{
using(var up = new UserPrincipal(pc))
{
up.SamAccountName = username;
up.EmailAddress = email;
up.SetPassword(password);
up.Enabled = true;
up.ExpirePasswordNow();
up.Save();
}
}
I'd use #Nick's code (wrapped in using statements so the context and principal are disposed properly). As for privileges, you'll need to at least have enough privileges on the OU where you are creating the user to create and manage objects. I'd create a specific user under which your program will run and give it just enough privileges to do the tasks that it needs in that specific OU and no more.
Yes can also use below code to create bulk of users
DirectoryEntry ouEntry = new DirectoryEntry("LDAP://OU=TestOU,DC=TestDomain,DC=local");
for (int i = 0; i < 10; i++)
{
try
{
DirectoryEntry childEntry = ouEntry.Children.Add("CN=TestUser" + i, "user");
childEntry.CommitChanges();
ouEntry.CommitChanges();
childEntry.Invoke("SetPassword", new object[] { "password" });
childEntry.CommitChanges();
}
catch (Exception ex)
{
}
}
The actual attribute for the password is unicodePwd, which requires a specific format that is described in the documentation. But this is how you can do it:
newUser.Properties["unicodePwd"].Value = Encoding.Unicode.GetBytes("\"NewPassword\"");
Doing it this way, you can create the user with a password in one step.

Generic Authentication Call to Active Directory in C#

I would like to have a clean C# class that authenticates from Active Directory.
It should be pretty simple, it just has to ask for credentials and check if it matches what AD is expecting.
I am responsible for a number of C# applications, and I would like all of them to use the same class.
Could someone please provide a clean code sample of such a class? It should have good error handling, be well commented, and specifically ask for credentials rather than try to read if a user is already logged in to AD for another application. (This is a security requirement because some applications are used in areas with shared computers: People with multiple roles and different permission levels may use the same computer and forget to log out between sessions)
http://support.microsoft.com/kb/316748
public bool IsAuthenticated(String domain, String username, String pwd)
{
String domainAndUsername = domain + "\\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, 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;
}
Admittedly I have no experience programming against AD, but the link below seems it might address your problem.
http://www.codeproject.com/KB/system/everythingInAD.aspx#35
There's some reason you can't use Windows integrated authentication, and not bother users with entering their names and passwords? That's simultaneously the most usable and secure solution when possible.

Authenticating an ADAM user against ADAM from C# - cannot bind

I have set up an ADAM instance and added some test users. From c# I can bind to ADAM using a windows account but I cannot bind using one of the ADAM users. (I can successfully bind the adam users in ldp) & I have made sure the users are enabled by setting msDS-UserAccountDisabled attribute to false.
When I bind with my windows account I can successfully search & bring back properties for ADAM users but I am still struggling to authenticate them, when I try and bind with an ADAM user account I get the error :
Error: System.Runtime.InteropServices.COMException (0x8007052E): Logon failure: unknown user name or bad password. at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
Here is the code I am using:
string userName = txtUserName.Text;
string password = txtPassword.Text;
string ADConnectionString = "LDAP://localhost:389/CN=sandbox,DC=ITOrg";
DirectoryEntry entry = new DirectoryEntry(ADConnectionString);
entry.Username = "myComputer\\Administrator";
entry.Password = "myPassword";
try
{
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(&(objectClass=user)(CN=" + userName + "))";
SearchResultCollection result = searcher.FindAll();
if (result.Count > 0)
{
//bind with simple bind
using (DirectoryEntry de = new DirectoryEntry(result[0].Path, userName, password,AuthenticationTypes.None))
{
if (de.Guid != null) // this is the line where it dies
{
Label1.Text = "Successfully authenticated";
Label2.Text = result[0].Properties["displayName"][0].ToString();
Label3.Text = result[0].Properties["telephoneNumber"][0].ToString();
} else
{
Lable1.Text = "Unable to Authenticate";
}
}
}
else
{
Lable1.Text = "UserName :" + userName + " not found";
}
} catch(Exception ex)
{
Label1.Text = "Error searching: " + ex.ToString();
}
Thanks in advance for any help, much appreciated!
It is probably a username format problem. When authenticating an ADAM user in SDS, you must use LDAP simple bind and use a name format supported by ADAM. ADAM technically allows you to use Digest auth as well, but that is not available in SDS (only SDS.Protocols), so that doesn't apply to your code approach.
You ARE using simple bind because you have AuthenticationTypes.None set so that part is ok. The part that is likely wrong then is the username format.
ADAM accepts the user's full DN, their displayName (if set and unique) and/or the userPrincipalName (if set and unique) as a "bindable" username, so start with the full DN of the user and seee if that works. If so, you can try the other user name values as well. Note that you can put whatever you want for displayName or userPrincipalName in ADAM. There is no validation. Just make sure the values are unique.
If you really want to do some type of bind authentication thing against ADAM, you'll get better perf and scale by using the ValidateCredentials method of PrincipalContext in .NET 3.5.
This kind of stuff is documented and discussed in the forums over at http://www.directoryprogramming.net all the time and is a place I frequent much more often since it is my site. :) A friend tipped me off to this post or I would have never seen it.

Categories