• 06Jan
    Author: ben Categories: Infrastructure Comments: 2

    I often work in hybrid environments with lots of Linux/UNIX servers to do heavy lifting and a large Windows domain for workstations, directory services, and Exchange. Interoperability between the environments is important, and there is the occasional need for a web application or script that can change a user’s AD password. Here’s a breakdown of a rough script that does just that.

    The script takes the following approach:

    • Accept inputs from the command line (this is a rough script – no fancy usage flags or getopt argument parsing).
    • Bind to the directory.
    • Search for the user, confirm only one account matching the constraints exists, and pull the DN attribute.
    • Create a double quoted unicode version of the passwords (Active Directory requires this for the account’s current and new passwords).
    • Delete, then Add the unicodePwd attribute from the account. This is must be done in a single LDAP modify operation.

    In the form of the script below, a user can change their own AD password, but a privileged account (such as a domain admin) cannot. Users change their own password with a delete, followed by an add. Admins change passwords with an LDAP replace. Uncomment the appropriate line in the script to adjust the functionality. Read on for the full script.

    #!/usr/bin/perl -w
    # ad_pwchange.pl - Ben Whaley - ben at atrust dot com
    use strict;
    use Net::LDAPS;
    
    my ($binddn, $bindpw, $searchdn, $server, $sam, $newpass);
    my ($ad, $uninewpass, $unibindpw, $sr, $samdn, $rtn);
    
    $binddn = $ARGV[0]; # distinguished name for the LDAP bind
    $bindpw = $ARGV[1]; # a password to match $binddn
    $searchdn = $ARGV[2]; # The search base, usually the AD domain name
    $server = $ARGV[3]; # The AD server to bind to (over LDAPS, or port 636)
    $sam = $ARGV[4]; # The sAMAccountName, or username, of the account
    $newpass = $ARGV[5]; # The new password value
    
    # Bind to the AD server
    $ad = Net::LDAPS->new($server, version => 3) or exit 1;
    $ad->bind(dn => $binddn, password => $bindpw) or exit 1;
    
    # Do an AD lookup to get the dn for this user.
    # also, confirm only 1 account matches.
    $sr = $ad->search(base => $searchdn, filter => "samaccountname=$sam");
    die 1 if ($sr->count() !=1 );
    $samdn = $sr->entry(0)->dn;
    
    # Add quotes and uniCode to the passwords.
    map { $uninewpass .= "$_\000" } split(//, "\"$newpass\"");
    map { $unibindpw .= "$_\000" } split(//, "\"$bindpw\"");
    
    # Uncomment this (and comment the next lines)
    # to allow admin password changes
    #$rtn = $ad->modify($samdn, replace => [ 'unicodePwd' => $uninewpass]);
    
    # Replace the password in an LDAP modify operation
    $rtn = $ad->modify($samdn, changes => [
           delete => [ 'unicodePwd' => $unibindpw ],
           add => [ 'unicodePwd' => $uninewpass]
           ]);
    
    # Complain verbosely if there was an error
    if($rtn->{'resultCode'} != 0) {
        print $rtn->error . "\n";
        exit 1;
    }
    exit 0;

    An example usage for a user password change might be:

    % ./ad_pwchange.pl 'CN=Whaley\, Benjamin,OU=Users,DC=example,DC=com'
     'CurrentPass' 'DC=example,DC=com' 'adserver.example.com' 'bwhaley' 'NewPass'

    Similarly, for an admin changing another user’s password:

    % ./ad_pwchange.pl 'CN=Admin\, Super,OU=Admins,DC=example,DC=com'
     'AdminPass' 'DC=example,DC=com' 'adserver.example.com' 'bwhaley' 'NewPass'

    I typically call this code from other scripts (sometimes from PHP-based web apps), thus the lack of arguments, prompted inputs, dual password verification, and better error handling.

    [Slashdot] [Digg] [Reddit] [del.icio.us] [Technorati] [StumbleUpon]

2 Responses

WP_Floristica

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.