ltb-project / self-service-password

Web interface to change and reset password in an LDAP directory
https://self-service-password.readthedocs.io/en/latest/
GNU General Public License v3.0
1.13k stars 319 forks source link

How to honor password history restriction (Active Directory + password change as manager) #927

Open ianharrier opened 6 days ago

ianharrier commented 6 days ago

I just wanted to put this out there, as I've seen password history mentioned in previous issues, and in case the maintainers have any interset in making this an official feature.


In Self Service Password v1.5 and below, I was able to replace this (https://github.com/ltb-project/self-service-password/blob/1.5/lib/functions.inc.php#L523-L527):

        } else {
            ldap_mod_replace($ldap, $dn, $userdata);
            $error_code = ldap_errno($ldap);
            $error_msg = ldap_error($ldap);
        }

with this:

        } else {
            # LDAP_SERVER_POLICY_HINTS_OID for Windows 2012 and above
            # Enforces password history and minimum age
            $histctrl = array(
                "oid" => "1.2.840.113556.1.4.2239",
                "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, 1)
            );
            $sethistctrl = ldap_set_option($ldap, LDAP_OPT_SERVER_CONTROLS, array($histctrl));

            ldap_mod_replace($ldap, $dn, $userdata);
            $error_code = ldap_errno($ldap);
            $error_msg = ldap_error($ldap);
        }

which would allow me to use $who_change_password = "manager"; while still enforcing Active Directory's password history restrictions.


In SSP v1.6, the password change code has been moved into https://github.com/ltb-project/ltb-ldap, so replacing this (https://github.com/ltb-project/ltb-ldap/blob/main/src/Ltb/Ldap.php#L349-L354):

    function modify_attributes($dn, $userdata): array {
        \Ltb\PhpLDAP::ldap_mod_replace($this->ldap, $dn, $userdata);
        $error_code = \Ltb\PhpLDAP::ldap_errno($this->ldap);
        $error_msg = \Ltb\PhpLDAP::ldap_error($this->ldap);
        return array($error_code, $error_msg);
    }

with this:

    function modify_attributes($dn, $userdata): array {
        # LDAP_SERVER_POLICY_HINTS_OID for Windows 2012 and above
        # Enforces password history and minimum age
        $histctrl = array(
            "oid" => "1.2.840.113556.1.4.2239",
            "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, 1)
        );
        $sethistctrl = ldap_set_option($this->ldap, LDAP_OPT_SERVER_CONTROLS, array($histctrl));

        \Ltb\PhpLDAP::ldap_mod_replace($this->ldap, $dn, $userdata);
        $error_code = \Ltb\PhpLDAP::ldap_errno($this->ldap);
        $error_msg = \Ltb\PhpLDAP::ldap_error($this->ldap);
        return array($error_code, $error_msg);
    }

now seems to accomplish the same thing.

coudot commented 4 days ago

Interesting page on LDAP controls for Active Directory : https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/3c5e87db-4728-4f29-b164-01dd7d7391ea?redirectedfrom=MSDN

coudot commented 4 days ago

See https://github.com/ltb-project/ltb-ldap/issues/16