Directory Programming .NET

Active Directory and ADAM programming support for .NET developers
Welcome to Directory Programming .NET Sign in | Join | Help
in Search

Wanting to force user to change password

Last post 03-19-2010, 8:57 AM by Casiel. 8 replies.
Sort Posts: Previous Next
  •  03-15-2010, 11:07 AM 7954

    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 7959 in reply to 7954

    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 7963 in reply to 7959

    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 7966 in reply to 7963

    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 7969 in reply to 7966

    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 7971 in reply to 7966

    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 7976 in reply to 7971

    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 7981 in reply to 7976

    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 7992 in reply to 7976

    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.
View as RSS news feed in XML