Search code examples
vb.netactive-directoryldappasswords

VBNet changing Active Directory passwords of expired or one time password accounts


I need to change the Passwords of an Active Directory account in windows 10 in VB.NET.

The program, I wrote, runs as local administrator,

My working code with a valid user account is (Domain_xps and UserName_xps are Strings and pwdPtr System.Runtime.InteropServices.Marshal.SecureStringToBSTR of a SecureString):

dEntry = New DirectoryServices.DirectoryEntry("LDAP://" & Domain_xps, UserName_xps
     , System.Runtime.InteropServices.Marshal.PtrToStringBSTR(pwdPtr)
     , System.DirectoryServices.AuthenticationTypes.Secure 
       + System.DirectoryServices.AuthenticationTypes.Sealing 
       + System.DirectoryServices.AuthenticationTypes.ServerBind) ',pwd)

nativeObject = dEntry.NativeObject

Dim searcher_Fullname_xpo As System.DirectoryServices.DirectorySearcher 
                             = New System.DirectoryServices.DirectorySearcher(dEntry)    
With searcher_Fullname_xpo    
  .Filter = "(&(objectClass=User) (sAMAccountName=" & UserName_xps & "))"
End With    
result_xpo = searcher_Fullname_xpo.FindOne
Dim user As DirectoryServices.DirectoryEntry 'open directory
user = result_xpo.GetDirectoryEntry() 'get directory results
user.Username = UserName_xps
user.Password = PWD_xps    
user.Path = result_xpo.GetDirectoryEntry().Path
user.AuthenticationType = System.DirectoryServices.AuthenticationTypes.Secure 
                          + System.DirectoryServices.AuthenticationTypes.Sealing 
                          + System.DirectoryServices.AuthenticationTypes.ServerBind
user.Options.PasswordPort = 389
user.Options.PasswordEncoding = 1
user.Invoke("ChangePassword", New Object() {PWD_xps, PWDNeu_xps})
user.CommitChanges() 'commit changes
user.Close() 'close directory

But if a account has expired through holidays or if a new user with a one time password is generated and tries to change his password, i get an error.

The user or password are wrong.

while debugging i noticed, that following lines produce the same error.

nativeObject = dEntry.NativeObject

Dim searcher_Fullname_xpo As System.DirectoryServices.DirectorySearcher = New System.DirectoryServices.DirectorySearcher(dEntry)    
With searcher_Fullname_xpo    
  .Filter = "(&(objectClass=User) (sAMAccountName=" & UserName_xps & "))"
End With    
result_xpo = searcher_Fullname_xpo.FindOne

And also

user.Options.PasswordPort = 389
user.Options.PasswordEncoding = 1
user.Invoke("ChangePassword", New Object() {PWD_xps, PWDNeu_xps})

each of these produce the error and doesn't change the password correctly, after encapsulating every line in a try catch expression.

Active Directory shows a change, but the user account is not valid any more.

I tried basically the same methods, in hope that i could set the options.

Dim ADS_OPTION_PASSWORD_PORTNUMBER As Long = 6
  Dim ADS_OPTION_PASSWORD_METHOD As Long = 7

  Dim ADS_PASSWORD_ENCODE_REQUIRE_SSL As Integer = 0
  Dim ADS_PASSWORD_ENCODE_CLEAR As Integer = 1
  Try
    user.Invoke("SetOption", New Object() {ADS_OPTION_PASSWORD_PORTNUMBER, 389})
  Catch ex3 As Exception

  End Try

  Try
    user.Invoke("SetOption", New Object() {ADS_OPTION_PASSWORD_METHOD, ADS_PASSWORD_ENCODE_CLEAR})
  Catch ex3 As Exception

  End Try

And i tried also

 user.Invoke("SetPassword", New Object() {PWDNeu_xps})

the error message stays the same

To set the password seems the right way, but as i can't set the password port or enable the password method, it produces the same error.

i also found this old thread How to change password in active directory when password expired

but that is not longer possible under windows 10.

How can i change the password, of an expired account with the needed options in VB Net or can i configure the account, so that it is possible to achieve it.

So an update on this situation. 28.10.2021

I also tried as workaround a powershell command as described in Microsoft or here for that matter.

And the same thing happens, the password change isn't possible,

Set-ADAccountPassword : The server has rejected the client credentials.

With a valid user the comand works as does it in DotNet


Solution

  • After long time struggling and long discussion with microsoft, it became clear that settings a passowrd without having an user account that has right privileges, would ot be possible with Dot Net.

    So i experimented further and found a practicable solution with a posershell comand.

    1. you need to install the Active directory package for powershell see https://learn.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2022-ps

    2. you need an Active directory user that can set password(ideally nothing more). as the password has to be saved on the computer where app is run from, it must comply with the password rules and should be changed according to SOPs regularly. The computer where the app runs must be secured, so that only personal with clearance can access it and run the program.

    The rest is a powershell command to change the password of a active directory user( AD), as i couldn't find any useful solution, you can find the command below.

    Dim execProcess As New System.Diagnostics.Process
    Dim psScriptTextArg = "-command " & Chr(34) & "$Credential = New-Object System.Management.Automation.PSCredential ('" & Domain_xps & "\" & superuserName_xps _
        & "' , (ConvertTo-SecureString -AsPlainText '" & SuperuserPWD_xps & "' -Force));" _
        & "Set-ADAccountPassword -Server '" & Domain_xps &
         "' -Credential $Credential  -Identity '" & UserName_xps & "' -OldPassword (ConvertTo-SecureString -AsPlainText '" & PWD_xps &
          "' -Force) -NewPassword (ConvertTo-SecureString -AsPlainText '" & PWDNeu_xps & "' -Force)" & Chr(34)
    
    powershell_xps = psScriptTextArg
    execProcess.StartInfo.FileName = "powershell.exe"
    execProcess.StartInfo.Arguments = psScriptTextArg
    execProcess.StartInfo.UseShellExecute = True
    execProcess.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden
    execProcess.Start()
    

    if the computer has restricted access, the risk is acceptable and the evry user can set his own password even if his password was expired, without calling the it support.