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.
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;
Hi Remko,
I was just wondering how JwsclActiveDirectory is going? I'm keen to use it in a program I'm writing to dynamically add/remove users to groups, based on which computer they use to log into the Terminal Server.
Cheers,
Alex
I too am keen to use this, but i can't get it to compile.
There seems to be lots of dependencies missing. I am new to JEDI. Is there a step by step guide on where all the files should be placed?
I have added \WIN32API folder to my search path, but then i notice that it needs \WIN32API\jwaWindows folder as well. So i added this, then it complains that it cant find JwaWinDLLNames.pas... and it goes on.