Previously I discussed IDirectoryObject, today I will show how to change a user’s password with IDirectoryObject.

I didn’t find any documentation except a kb article describing how to use pure ldap to do it. Of course I could have used IADsUser::SetPassword but I decided not to because of the following reasons:

  • IADs interfaces are terribly slow (although for one use you probably wouldn’t really notice).
  • IADsUser::SetPassword tries 3 different methods to set the password (ldap over ssl, kerberos and finally NetUserSetInfo) which makes it even slower (most domain controllers do not have an ssl certificate) and unpredictable.

All example code I found was .NET based using the .NET wrappers for Active Directory and seemed to be meant for use in Adam rather than full Active Directory (it set port number to 389 and password mode to cleartext).

In the end it’s not very difficult but nonetheless it took me a while before I got it right.

We can write to the unicodePwd attribute which wants the password as a double quoted unicode string. If you look at this attribute with AdsiEdit you’ll see that the type is Octet String and that it can be written only.

I was tricked with Delphi’s QuotedStr function for a while because it doesn’t return a double but single quoted string ;-)

Below a small snippet from the upcoming JwsclActiveDirectory that shows how to use it:

procedure TJwAdUser.SetPassword(const Value: TJwString);
var
  AdValue: ADSVALUE;
  AdAttrInfo: ADS_ATTR_INFO;
  TempPwd: TJwString;
  DirObjIntf: IDirectoryObject;
  Count: DWORD;
  hr: HRESULT;
begin
  { To set the password we need a secure bind to the Domain Controller, this
    is possible from win2000 sp5 and higher }

  hr := ADsOpenObject(PWideChar(FPathName), nil, nil, ADS_USE_SIGNING
    or ADS_USE_SEALING or ADS_SECURE_AUTHENTICATION, IID_IDirectoryObject,
    Pointer(DirObjIntf));

  if failed(hr) then
    raise EJwsclAdNoInterfaceException.CreateFmtEx(
      ‘Interface %s not obtained’, ‘SetPassword’, ClassName,
      ‘JwsclActiveDirectory’, 0, hr, [‘IID_IDirectoryObject’]);

  { we can set the password through the unicodePwd attribute. }
  AdAttrInfo.pszAttrName := ‘unicodePwd’;
  AdAttrInfo.dwControlCode := ADS_ATTR_UPDATE;
  AdAttrInfo.pADsValues := @AdValue;
  AdAttrInfo.dwNumValues := 1;

  { the password needs to be within a double quoted string }
  TempPwd := AnsiQuotedStr(Value, ‘"’);

  { unicodePwd is of type Octet String }
  AdValue.dwType := ADSTYPE_OCTET_STRING;
  AdValue.OctetString.dwLength := Length(TempPwd) * sizeof(TempPwd[1]);
  AdValue.OctetString.lpValue := @TempPwd[1];

  { set the password }
  hr := DirObjIntf.SetObjectAttributes(@AdAttrInfo, 1, Count);

  { don’t keep the password in memory… }
  SecureZeroMemory(@TempPwd[1], Length(TempPwd) * sizeof(TempPwd[1]));

  DirObjIntf := nil;

  if failed(hr) then
    raise EJwsclAdSetPropertyException.CreateFmtEx(
      ‘SetObjectAttributes failed to set %s’, ‘SetPassword’, ClassName,
      ‘JwsclActiveDirectory’, 0, hr, [AdAttrInfo.pszAttrName]);
end;

Related posts:

  1. PowerShell 2.0: Changing password through ADSI problem
  2. Random Active Directory Notes #1
  3. Random Active Directory Notes
  4. Random Active Directory Notes #2
  5. Random Active Directory Notes #3