Reset Password in Active Directory

Last post 05-09-2008 4:02 PM by aaguirre. 20 replies.

Sort Posts:

  • Reset Password in Active Directory

    12-19-2002, 12:36 PM
    • Loading...
    • JRing
    • Joined on 06-27-2002, 4:57 PM
    • Posts 5
    Okay,

    I have been trying for months to find the correct code to reset a user's password in Active Directory. We're trying to do this using ASP.net and C#. Here is what I have so far:

    ActiveDs.IADsUser User = (ActiveDs.IADsUser)myEntry.NativeObject;
    User.SetPassword(newPassword);
    User.SetInfo();
    user.CommitChanges();

    We've also tried this:

    user.Invoke("SetPassword", new object[] {newPassword});
    user.CommitChanges();

    Both work (sometimes) from my machine (inside our firewall), but once we get outside, this stuff doesn't work. I am passing userID and password to the DirectoryEntry call and setting the AuthenticationType to Secure.

    We can get the 2nd code piece to work fine if using "ChangePassword", but we need this to work if we don't have the user's old password.

    Any help will be much, much appreciated.

    Thanks,
    Josh
  • Re: Reset Password in Active Directory

    12-19-2002, 1:00 PM
    • Loading...
    • dunnry
    • Joined on 06-24-2002, 4:17 PM
    • http://directoryprogramming.net
    • Posts 1,806
    Calling 'SetPassword' needs two things: Kerberos and the correct permissions. You must be an adminstrator to call 'SetPassword' - which makes sense if you think about it. If you are passing the user's credentials to the DirectoryEntry and binding with them, you will be binding with the user's permissions and I doubt Adminstrator is one of them. This would also be why 'ChangePassword' works, because this call is intended for user's to change their own password. 'SetPassword' is much more powerful as it can override any password policy settings you have in place.

    Include your code for the DirectoryEntry and I might be able to point out any other issues. Also make sure you include your exact connection string to your LDAP source in your post (it makes a difference).
  • Re: Reset Password in Active Directory

    01-03-2003, 11:01 AM
    • Loading...
    • JRing
    • Joined on 06-27-2002, 4:57 PM
    • Posts 5
    Thank you so much for your help so far, here is my DirectoryEntry call:

    DirectoryEntry myEntry = new DirectoryEntry(domainPath, userID, password, AuthenticationTypes.Secure);

    DirectoryEntries myEntries = myEntry.Children;

    I'm assuming that this is the correct authentication type since it does mention Kerberos in its' description but please feel free to correct me if necessary.

    Thanks again!
  • Re: Reset Password in Active Directory

    01-03-2003, 1:14 PM
    • Loading...
    • dunnry
    • Joined on 06-24-2002, 4:17 PM
    • http://directoryprogramming.net
    • Posts 1,806
    This is a more minor point, but will get you later if you aren't aware of it:

    Make sure you either connect using serverless bind, or if using server name - that it is fully qualified in your constructor to ensure that Kerberos will be used . If you only use the netbios name or short name of your server, it will use SPNEGO, which can mean that NTLM will possibly be selected (and you will get "exception has been thrown by target of invokation..." error).

    In your case here, the contructor looks fine if the userID, and password you are putting into the constructor are the credentials for a domain Admin. The steps to reset password are these:

    1. Create a main DirectoryEntry, binding with appropriate credentials (that of an Admin if using SetPassword).
    2. Find the DirectoryEntry of the user object you want to change password of - using either DirectoryEntry.Children.Find or a DirectorySearcher.
    3. Using the object in #2, do your .Invoke("SetPassword", new object[]{"newPassword"})
    4. Commit the changes.

    The steps change slightly if you want to use "ChangePassword" and the user should change his own:

    1. Create a main DirectoryEntry using *any* valid credentials on the domain.
    2. Find the DirectoryEntry for the user that will have password changed.
    2a. *edit*, bind the user object with their credentials explicitly (.Username, .Password)
    3. Using object in #2, do .Invoke("ChangePassword", new object[]{"oldPass","newPassword"})
    4. Commit the changes.

    Read the minor point again above if you keep getting errors... And always test from remote machine and not directly from server.


  • Re: Reset Password in Active Directory

    01-16-2003, 8:58 AM
    • Loading...
    • JRing
    • Joined on 06-27-2002, 4:57 PM
    • Posts 5
    That seems to be the error I'm getting. You mentioned target invocation and indeed when it kicks out an error to me, it's System.TargetInvocationException. I'm really stumped as to what I'm doing wrong. I'm not specifying a server in my domainPath, just the domain name. Any other words of advice would be much appreciated.

    Thanks again,
    Josh
  • Re: Reset Password in Active Directory

    01-16-2003, 10:07 AM
    • Loading...
    • dunnry
    • Joined on 06-24-2002, 4:17 PM
    • http://directoryprogramming.net
    • Posts 1,806
    Which method is failing (SetPassword() or ChangePassword())?

  • Re: Reset Password in Active Directory

    01-16-2003, 10:11 AM
    • Loading...
    • JRing
    • Joined on 06-27-2002, 4:57 PM
    • Posts 5
    SetPassword is failing
  • Re: Reset Password in Active Directory

    01-16-2003, 10:26 AM
    • Loading...
    • dunnry
    • Joined on 06-24-2002, 4:17 PM
    • http://directoryprogramming.net
    • Posts 1,806
    Ok, I spent an inordinate amount of time on this one as well since I last posted for a similar site that allows users to change and update passwords. It turns out that in addition to all the things I have listed above, there is one other major (and painful) requirement:

    SetPassword() cannot be called unless the process calling it also has Administrator credentials (i.e. the process token has Reset password right). That means that regardless of what you are passing into the Constructor, it will fail with that "Error has been thrown by target of invokation" in ASP.NET because the aspnet_wp.exe runs always under ASPNET user account context (it would not be a good idea to change this either).

    There are two solutions that I have found that will allow this to work:

    1. Put the code into COM+ and set an identity of an Admin, it will run in that context and work fine.
    2. Impersonate an Admin for duration of SetPassword() call using Interop. This is not the same as using <identity impersonate="true" /> tag. This type of impersonation I am talking about actually changes Process token to Admin.

    The first one takes a little bit of work to get going (creating and registering .snk file), and there are a couple minor limitations on COM+ objects. It also makes future changes to the website a little more tedious as you will have to register the COM+ object manually (no xcopy deployment here).

    The second option lets you keep the drag and drop deployment, but on Windows 2000, requires you to give additional priviledges to the ASPNET account to make use of the underlying API that is required for impersonation. If you are using XP or 2003 server, then it does not have this requirement.

    Think about which way you would like to go and I can show you how to do either.

  • Re: Reset Password in Active Directory

    01-16-2003, 4:26 PM
    • Loading...
    • JRing
    • Joined on 06-27-2002, 4:57 PM
    • Posts 5
    Well, speaking from experience, an extra component that requires registering with COM+ is a major pain so I think we'd be more interested in manipulating the ASPNET account. If you could further explain what it is we need to do I would be much appreciative.

    Thanks again for all your help,
    Josh
  • Re: Reset Password in Active Directory

    01-17-2003, 1:57 PM
    • Loading...
    • dunnry
    • Joined on 06-24-2002, 4:17 PM
    • http://directoryprogramming.net
    • Posts 1,806
    Ok, here is something that should work for you:

    [DllImport("C:\\WINNT\\System32\\advapi32.dll")]
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
    int dwLogonType, int dwLogonProvider, out IntPtr phToken);

    [DllImport("C:\\WINNT\\System32\\Kernel32.dll")]
    public static extern int GetLastError();

    [DllImport("kernel32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);

    const int UF_SCRIPT = 0x0001;
    const int UF_ACCOUNTDISABLE = 0x0002;
    const int UF_HOMEDIR_REQUIRED = 0x0008;
    const int UF_LOCKOUT = 0x0010;
    const int UF_PASSWD_NOTREQD = 0x0020;
    const int UF_PASSWD_CANT_CHANGE = 0x0040;
    const int UF_TEMP_DUPLICATE_ACCOUNT = 0x0100;
    const int UF_NORMAL_ACCOUNT = 0x0200;
    const int UF_INTERDOMAIN_TRUST_ACCOUNT = 0x0800;
    const int UF_WORKSTATION_TRUST_ACCOUNT = 0x1000;
    const int UF_SERVER_TRUST_ACCOUNT = 0x2000;
    const int UF_DONT_EXPIRE_PASSWD = 0x10000;
    const int UF_MNS_LOGON_ACCOUNT = 0x20000;

    //get ahold somehow of the user that you want to reset their password for
    DirectoryEntry _user = new DirectoryEntry(adPath, adminUsername, adminPassword, AuthenticationTypes.Secure);

    /// <summary>
    /// Reset the user's password
    /// </summary>
    /// <param name="reset">(bool) change password at next login</param>
    /// <returns>user's password (string)</returns>
    public string ResetPassword(bool reset)
    {
    string sPwd = _user.Properties["sAMAccountName"][0].ToString() + ".tmp"; //static password here
    int flags;

    if(reset)
    {
    //first have to remove "Password Never Expires Flag"
    flags = (int)_user.Properties["userAccountControl"].Value;
    if(Convert.ToBoolean(flags & UF_DONT_EXPIRE_PASSWD))
    {
    flags = (flags ^ UF_DONT_EXPIRE_PASSWD);
    _user.Properties["userAccountControl"].Value = flags;
    }

    if(_user.Properties.Contains("pwdLastSet"))
    _user.Properties["pwdLastSet"].Value = 0;
    else
    _user.Properties["pwdLastSet"].Add(0);
    }
    else
    {
    //clear the change password at next login if it is there
    if(_user.Properties.Contains("pwdLastSet"))
    _user.Properties["pwdLastSet"].Value = -1;
    else
    _user.Properties["pwdLastSet"].Add(-1);

    //set the password never expires flag.
    flags = (int)_user.Properties["userAccountControl"].Value;
    if(!Convert.ToBoolean(flags & UF_DONT_EXPIRE_PASSWD))
    {
    flags = (flags | UF_DONT_EXPIRE_PASSWD);
    _user.Properties["userAccountControl"].Value = flags;
    }
    }

    //Change thread context to Admin's **IMPERSONATION CODE STARTS HERE**
    IntPtr token = IntPtr.Zero;
    string username = ""; //same as in your _user constructor
    string domain = ""; //same as in your _user constructor

    bool result = LogonUser(username, domain , Config.Settings.AdminPassword, 3, 0, out token);
    if(!result)
    {
    int errCode = GetLastError();
    string errMessage = String.Empty;
    switch(errCode)
    {
    case 5:
    errMessage = "Access Denied";
    break;
    case 1326:
    errMessage = "Logon failure: unknown user name or bad password.";
    break;
    }
    throw new Exception(String.Format("GetLastError() returned {0}, \"{1}\"", errCode, errMessage));
    }
    else
    {
    WindowsIdentity wi = new WindowsIdentity(token);
    WindowsImpersonationContext wic = wi.Impersonate();
    _user.Invoke("SetPassword", new object[]{sPwd.ToLower()});
    _user.CommitChanges();

    wic.Undo(); //end impersonation **END IMPERSONATION**
    CloseHandle(token);
    }

    return sPwd.ToLower();
    }

    Ok, so there is a bit of code here... just declare the DllImports and see how I am using the Impersonation code. I have some more stuff in there because I need it to do a couple more things (like set "Change Password at next Login").

    Good luck, it is a little hard to cut & paste into this window, so I hope I did not leave anything out (it is part of much bigger class).
  • Re: Reset Password in Active Directory

    01-19-2003, 1:43 PM
    • Loading...
    • bdesmond
    • Joined on 06-15-2002, 6:02 PM
    • Chicago, IL USA
    • Posts 944
    • ControlGallery
      TrustedFriends-MVPs
    I implemented a similiar solution with LogonUser. If you're running a 2000 server, ASPNET needs act as part of the operating system rights in order to call that api.
    --Brian Desmond
    Windows Server MVP - Directory Services
    http://www.briandesmond.com
  • Re: Reset Password in Active Directory

    01-19-2003, 9:50 PM
    • Loading...
    • dunnry
    • Joined on 06-24-2002, 4:17 PM
    • http://directoryprogramming.net
    • Posts 1,806
    Yes, that is correct if you are using Windows 2000. I had alluded to this in an earlier post, but forgot to mention what that right was. Thanks for the catch. If you are running 2003 Server or XP, you don't need to grant this right.
  • Re: Reset Password in Active Directory

    01-20-2003, 11:40 AM
    • Loading...
    • bdesmond
    • Joined on 06-15-2002, 6:02 PM
    • Chicago, IL USA
    • Posts 944
    • ControlGallery
      TrustedFriends-MVPs
    FYI you can assign Act as Part of OS rights by:

    Opening up the Group Policy console on the server
    Local Computer Policy
    Computer Config
    Windows Settings
    Sec Settings
    Local Policies
    User Rights Assignment
    Add ASPNET to the Act as Part of Operating System Assignment.

    The server has to be rebooted for the change to become effective.
    --Brian Desmond
    Windows Server MVP - Directory Services
    http://www.briandesmond.com
  • Re: Reset Password in Active Directory

    01-27-2003, 9:21 AM
    Hi, we had a similar problem. You don't have to call CommitChanges() This only applies to Properties.

    Hope it helps
    Marius Filipowski


    (mcad, mcsd)
  • Re: Reset Password in Active Directory

    01-27-2003, 11:29 AM
    • Loading...
    • dunnry
    • Joined on 06-24-2002, 4:17 PM
    • http://directoryprogramming.net
    • Posts 1,806
    If you are only calling SetPassword, that is correct. However, in this situation, it is necessary - you can see from the code above that other properties are being set.