Home > Net >  Validate an expired ActiveDirectory password using PowerShell or LDAP
Validate an expired ActiveDirectory password using PowerShell or LDAP

Time:07-27

As title said, I need a way to validate an expired password against ActiveDirectory. It has to be LDAP, or PowerShell, because the user will perform validation via a NodeJS service from another machine running Linux.

I have tried using System.DirectoryServices.DirectoryEntry like this below, but it can't validate an expired one. I want the user to replace their own expired password, but not before it is validated.

async testPassword(user, password) {
    const testCommand = `(New-Object System.DirectoryServices.DirectoryEntry '', '${ user }' , '${ password }')`;
    try {
        const result = await this.exec(testCommand);
        return result;
    } catch (err) {
        throw new Error(err);
    }
}

Most of the solution involved a dll library, which is not available in Linux, the second solution I found involves re-enabling the password before validating, which is not ideal. The other questions simply has no answers. I don't mind an error message or anything, as long as I can distinguish between wrong password and correct and expired password. And it has to work in linux ecosystem.

CodePudding user response:

You can use a node.js package to do all of this, rather than calling out to PowerShell to use a .NET class. For example, you can do the job with the activedirectory2 package.

There is an example of how authenticate in this answer. You'll notice that the error message in that answer has data 52e. That is the reason for the failure. The 52e is the hex value of the Windows System Error Code, which can be looked up here. All the reasons for failure are listed there too. For example, 532 would mean "The password for this account has expired."

For some reason, DirectoryEntry doesn't give you that information.

Then, if you want to change the user's password (using the old, correct password), you can use the code in this GitHub issue, which I'll paste below too. You must be connected via an encrypted method to change or reset the password, such as LDAPS.

 /**
 * Change user password
 *
 * WARN : this function needs to an LDAPS connection to works. 
 *
 * @public
 * @param {String} username The username to retrieve the user.
 * @param {String} oldPassword The oldPassword to authenticate user.
 * @param {String} newPassword The new password to change user's password.
 * @param {Function} callback The callback to execute when the changePassword is completed. callback(err: {Object}, result: {Object})
 */
ActiveDirectory.prototype.changePassword = function changePassword(username, oldPassword, newPassword, callback) {
  /**
   * Inline function to encode string to base 64
   *
   * @private
   */
  function encodePassword(password) {
    return new Buffer('"'   password   '"', 'utf16le').toString();
  }

  var client = createClient.call(this);

  client.search(this.baseDN, {
    filter: '(cn='   username   ')',
    attributes: 'dn',
    scope: 'sub'
  }, function(err, res) {
    if (err) {
      callback(err);
      return;
    }
    res.on('searchEntry', function(entry) {
      var userDN = entry.object.dn;
      client.bind(userDN, oldPassword, function(err) {
        if (err) {
          callback(err);
          return;
        }
        client.modify(userDN, [
          new ldap.Change({
            operation: 'delete',
            modification: {
              unicodePwd: encodePassword(oldPassword)
            }
          }),
          new ldap.Change({
            operation: 'add',
            modification: {
              unicodePwd: encodePassword(newPassword)
            }
          })
        ], function(err) {
          if (err) {
            callback(err);
          } else {
            callback();
            client.unbind();
          }
        });
      });
    });
  });
};
  • Related