|
|
Wanting to force user to change password
Last post 03-19-2010, 8:57 AM by Casiel. 8 replies.
-
03-15-2010, 11:07 AM |
-
Casiel
-
-
-
Joined on 03-11-2010
-
-
Posts 7
-
-
|
Wanting to force user to change password
We are developing a application with AD user validation.
One of the features I must add to the application is alowing the user to change the AD password from our application. In order to do this I have a domain server to test my loggin system.
Now, I've created a User and checked the "User must change password at next logon". When I input the correct password, the system gives me the same DirectoryServicesCOMException with the same error code. What am I missing, shouldn't the system throw a different error code for the tow different scenarios?
I authenticate the users like this.
public User Authenticate(string user, string pasword)
{
DirectorySearcher _searcher = new DirectorySearcher();
//Tomar en cuenta el dominio para que la query se haga contra el servidor correcto
String LDAPdomain = String.Concat("LDAP://", ConfigurationManager.AppSettings["LDAPDomainServer"]);
//
DirectoryEntry _entry = new DirectoryEntry(LDAPdomain)
{
Username = user,
Password = pasword,
AuthenticationType = AuthenticationTypes.Secure
};
_searcher.SearchRoot = _entry;
_searcher.Filter = String.Format("(sAMAccountName={0})", user);
_searcher.PropertiesToLoad.Add("objectguid");
_searcher.PropertiesToLoad.Add("name");
try
{
SearchResult _result = _searcher.FindOne();
var returnValue = new User();
if (_result != null)
{
returnValue.UserKey = new Guid((byte[])_result.Properties["objectguid"][0]).ToString();
using (var uc = new UserController())
{
returnValue = uc.GetFullById(uc.GetUser(String.Concat(userDomain, "\\", user), returnValue.UserKey).Id);
}
returnValue.Name = _result.Properties["name"][0].ToString();
return returnValue;
}
throw SecurityException.Login();
}
catch (DirectoryServicesCOMException comex)
{
throw SecurityException.Login(String.Concat(comex.Message, "#", comex.ErrorCode));
}
}
this works fine when authenticating a user, but when the user must change his/her password I get the same exception as if I enter a wrong password
On the other hand, I'm bypassing this point and trying to change the users password like this:
public bool ChangeUserPassword(string user, string oldPassword, string newPassword)
{
var LDAPdomain = String.Concat("LDAP://", ConfigurationManager.AppSettings["LDAPDomainServer"], "/CN=", user,",CN=users,DC=dev,DC=com");
var _entry = new DirectoryEntry(LDAPdomain)
{
Username = user,
Password = oldPassword,
AuthenticationType = AuthenticationTypes.Secure
};
try
{
_entry.Invoke("ChangePassword", new object[] { oldPassword, newPassword });
_entry.Close();
return true;
}
catch (DirectoryServicesCOMException comex)
{
throw SecurityException.Login(String.Concat(comex.Message, "#", comex.ErrorCode));
}
catch(COMException cex)
{
throw SecurityException.Login(String.Concat(cex.Message, "#", cex.ErrorCode));
}
catch(TargetInvocationException tex)
{
throw SecurityException.Login(String.Concat(tex.InnerException.Message, "#", tex.InnerException.StackTrace));
}
}
I'm passing hte same user and oldpassword with witch I authenticate and a newpassword like this: J053#O7OElP77 after authenticating. This last function throws me an exception saying that the password does not match the policies, the oldpassword beeing : Pru38@5#ZOO1
Any thougths would be appreciated
Regards.
Casiel.
|
|
-
03-16-2010, 10:56 AM |
-
joe
-
-
-
Joined on 04-05-2006
-
Chicago, IL
-
Posts 2,866
-
-
|
Re: Wanting to force user to change password
The setting for "user must change password at next logon" is not supported through LDAP-based password changes. As far as I know, you can only "undo" this setting through an interactive login. If the user is in the state, you'll get the same generic error as if the user had provided invalid credentials.
|
|
-
03-16-2010, 12:07 PM |
-
Casiel
-
-
-
Joined on 03-11-2010
-
-
Posts 7
-
-
|
Re: Wanting to force user to change password
Thank you for your repaly.
I'm sorry, I re read my post and it is not clear.
The exception in the same introducing the correct password (the one that must be changed) or an incorrect password. The server does not make a difference between the two scenarios.
|
|
-
03-17-2010, 8:54 AM |
-
joe
-
-
-
Joined on 04-05-2006
-
Chicago, IL
-
Posts 2,866
-
-
|
Re: Wanting to force user to change password
Does your domain have a minimum password age? That is frequently the cause of errors related to password policy where the password value itself looks fine. It is common to only allow 1 password change per day.
|
|
-
03-17-2010, 9:55 AM |
-
Casiel
-
-
-
Joined on 03-11-2010
-
-
Posts 7
-
-
|
Re: Wanting to force user to change password
Thanks for the replay, joe.
I'll check what you ask me.
On the other hands at the moment of LogIn I enter the correct UserName and the correct password (the password is the one that must be changed) and the server throws an exception saying "Unknown user or incorrect password" insted of saying something like "The user is requiered to change his/her password at first Logon" and I don't understand why. I need to be able to distinguish this case from the common mistaken password exception. I don't know if I am explaining myself clearly (English is not my native languge)
Regards.
Casiel.
|
|
-
03-17-2010, 10:44 AM |
-
Casiel
-
-
-
Joined on 03-11-2010
-
-
Posts 7
-
-
|
Re: Wanting to force user to change password
I've checked what you asked, and no, it doesn't have a minimum password age, and there is no policy on the amount of password changes per day.
In order to try and solve this, I've tryed to find out if the password has expired (as explained in chapter 10 of your book) but I keep reciving the same exception, "Unknown user or incorrect password", so I can't find out if the user must change his/her password.
Regards.
Casiel.
|
|
-
03-17-2010, 5:26 PM |
-
joe
-
-
-
Joined on 04-05-2006
-
Chicago, IL
-
Posts 2,866
-
-
|
Re: Wanting to force user to change password
I went back and looked at your code more carefully. It looks like you are trying to access the directory using the user's old credential when they are in the state "user must change password at next login". I'm suggesting that based on this code:
public bool ChangeUserPassword(string user, string oldPassword, string newPassword) { var LDAPdomain = String.Concat("LDAP://", ConfigurationManager.AppSettings["LDAPDomainServer"], "/CN=", user,",CN=users,DC=dev,DC=com"); var _entry = new DirectoryEntry(LDAPdomain) { Username = user, Password = oldPassword, AuthenticationType = AuthenticationTypes.Secure }; try { _entry.Invoke("ChangePassword", new object[] { oldPassword, newPassword }); _entry.Close();
The problem here is that the DirectoryEntry _entry cannot use the old password as a valid credential to authenticate to the directory if the user is in that state. Users cannot authenticate via LDAP at all and thus cannot perform ANY operations using their plaintext credentials if they have any kind of account problem (such as "must change password at next logon").
The solution for you might be to use a known good service account to access the directory.
|
|
-
03-18-2010, 9:52 AM |
-
Casiel
-
-
-
Joined on 03-11-2010
-
-
Posts 7
-
-
|
Re: Wanting to force user to change password
Thank you very much for your reply. I'll try and do what you say.
Regards.
Casiel
|
|
-
03-19-2010, 8:57 AM |
-
Casiel
-
-
-
Joined on 03-11-2010
-
-
Posts 7
-
-
|
Re: Wanting to force user to change password
What I needed to do was validate users against AD and, if the user have an expired password, let him change it.
I've achived it based on chapter 10 of the book (Thank you very much) like this:
public class LogInManager
{
private static NetworkCredential credential;
public static LogInResponse Authenticate(string username, string password)
{
var retValue = new LogInResponse ();
var path = "LDAP://" + ConfigurationManager.AppSettings["LDAPDomain"];
var entry = new DirectoryEntry(path, username, password)
{
AuthenticationType =
AuthenticationTypes.ServerBind | AuthenticationTypes.Sealing |
AuthenticationTypes.Secure | AuthenticationTypes.Signing
};
var searcher = new DirectorySearcher(entry);
searcher.PropertiesToLoad.Add("objectguid");
searcher.PropertiesToLoad.Add("name");
searcher.Filter = "(sAMAccountName=" + username + ")";
try
{
var aux = entry.NativeObject;
var result = searcher.FindOne();
if (result != null)
{
retValue.UserName = username;
}
return retValue;
}
catch (DirectoryServicesCOMException)
{
return AuthenticateWithTrustedUser(username, password);
}
catch(COMException)
{
return AuthenticateWithTrustedUser(username, password);
}
}
private static LogInResponse AuthenticateWithTrustedUser(string username, string password)
{
if (credential == null)
{
credential = new NetworkCredential("Test", "Pru38@5#ZOO1");
}
const int UF_DONT_EXPIRE_PASSWD = 0x10000;
var retValue = new LogInResponse(LogInExceptions.UnknownUserNameOrIncorrectPassword);
var path = "LDAP://" + ConfigurationManager.AppSettings["LDAPDomain"];
var entry = new DirectoryEntry(path, credential.UserName, credential.Password)
{
AuthenticationType =
AuthenticationTypes.ServerBind | AuthenticationTypes.Sealing |
AuthenticationTypes.Secure | AuthenticationTypes.Signing
};
var searcher = new DirectorySearcher(entry);
searcher.PropertiesToLoad.Add("objectguid");
searcher.PropertiesToLoad.Add("name");
searcher.PropertiesToLoad.Add("sAMAccountName");
searcher.PropertiesToLoad.Add("pwdLastSet");
searcher.PropertiesToLoad.Add("userAccountControl");
searcher.Filter = "(sAMAccountType=805306368)";
try
{
var aux = entry.NativeObject;
var results = searcher.FindAll();
if (results.Count > 0)
{
foreach (var result in results)
{
var resultUserName = ((SearchResult)result).Properties["sAMAccountName"][0];
if (resultUserName.ToString() == username)
{
var flags = (int)((SearchResult)result).Properties["userAccountControl"][0];
if (!Convert.ToBoolean(flags & UF_DONT_EXPIRE_PASSWD))
{
var val = Convert.ToInt64(((SearchResult) result).Properties["pwdLastSet"][0]);
if (val == 0)
{
retValue.Exception = LogInExceptions.UserMustChangePassword;
}
}
}
}
}
return retValue;
}
catch (DirectoryServicesCOMException exception)
{
switch (exception.ErrorCode)
{
case -2147023570:
{
return new LogInResponse(LogInExceptions.UnknownUserNameOrIncorrectPassword);
}
}
return new LogInResponse(LogInExceptions.UnknownException);
}
catch (COMException)
{
return new LogInResponse(LogInExceptions.UnknownException);
}
}
public static bool ChangeUsersPassword(string username, string oldpassword, string newpassword)
{
var retValue = false;
if (credential == null)
{
credential = new NetworkCredential("Test", "Pru38@5#ZOO1");
}
var path = "LDAP://" + ConfigurationManager.AppSettings["LDAPDomain"];
var entry = new DirectoryEntry(path, credential.UserName, credential.Password)
{
AuthenticationType =
AuthenticationTypes.ServerBind | AuthenticationTypes.Sealing |
AuthenticationTypes.Secure | AuthenticationTypes.Signing
};
var searcher = new DirectorySearcher(entry) {Filter = "(sAMAccountName=" + username + ")"};
try
{
var obj = entry.NativeObject;
var result = searcher.FindOne();
if (result != null)
{
result.GetDirectoryEntry().Invoke("ChangePassword", new object[] { oldpassword, newpassword });
result.GetDirectoryEntry().Close();
retValue = true;
}
return retValue;
}
catch (COMException)
{
return false;
}
}
}
if the seccond function tells me that the user has his/hers password expired I use know credetials to change the password.
One tip, I've realized that changing the password from my application required a stronger password than the one you can set from the active directory.
So far, so good.
Now, my boss is asking me to use que credentials of the current windows user, so I don't have to hardcode the credentials nor put them anywhere some could find them, is there a way to do that. So far, I've read that it is not possible to get the credentials from the current windows user.
Regards.
Casiel.
|
|
|
|