perlactive-directorynetldap

MS AD2008 - Unable to force password change at next login using Perl Net::LDAPS and userAccountControl attribute


I am connecting to an Active Directory 2008 Domain Controller using perl Net::LDAPS and trying to set the "User must change password at next logon" account option and it's not working.

I am able to create, modify, delete, and move different objects but I am not able to get that change password setting to take!

This is what I am trying to do and it's not working:

Note: I am using a self written class wrapper for Net::LDAPS and the code below is boiled down to the bare bones of what I am trying to do.

# Binding to LDAP Directory:
$self->{LDAP_INSTANCE} = Net::LDAPS->new($host);
$self->{LDAP_INSTANCE}->bind(dn=>$dn, password=>$password, version=>3 )

my $rc =$self->{LDAP_INSTANCE}->modify(
    $DN_OF_USER_ACCOUNT, 
    [ replace => [userAccountControl => 0x00800000] ]
);
print $rc->error; # Results in an empty string / No error

# Note: I have also tried: hex(800000) instead of 0x00800000 as well.

I am binding with a domain administrator account and I have verified that the $DN_OF_USER_ACCOUNT is correct.


Solution

  • The userAccountControl attribute is a bitfield with many settings. Changing the value for the user to be 0x00800000 would be quite harmful since it would remove the default of 0x200 ADS_UF_NORMAL_ACCOUNT.

    The documentation for 0x00800000 is:

    0x00800000
    ADS_UF_PASSWORD_EXPIRED
    The user password has expired. This flag is created by the system using data from the Pwd-Last-Set attribute and the domain policy.

    If we look at pwdLastSet's documentation, we see:

    The date and time that the password for this account was last changed. [...] If this value is set to 0 and the User-Account-Control attribute does not contain the UF_DONT_EXPIRE_PASSWD flag, then the user must set the password at the next logon.

    So to expire the password, we need to set pwdLastSet to zero. This is substantiated by a blog entry from Scripting Guy who does it in VBScript.

    In Perl:

    # Binding to LDAP Directory:
    $self->{LDAP_INSTANCE} = Net::LDAPS->new($host);
    $self->{LDAP_INSTANCE}->bind(dn=>$dn, password=>$password, version=>3 )
    
    my $rc =$self->{LDAP_INSTANCE}->modify(
        $DN_OF_USER_ACCOUNT, 
        [ replace => [pwdLastSet => 0] ]
    );
    print $rc->error;