#!/usr/bin/perl # # Evan Hoffman (evanhoffman@evanhoffman.com) # 13 January, 2010 # # Base functionality from: http://search.cpan.org/~gbarr/perl-ldap/lib/Net/LDAP/FAQ.pod#..._in_MS_Active_Directory_? print "Content-Type: text/html\n\n"; use Net::LDAP; use Unicode::Map8; use Unicode::String qw(utf16); use MIME::Base64; use CGI::Lite; # build the conversion map from your local character set to Unicode my $charmap = Unicode::Map8->new('latin1') or die; my $host = 'ldaps://activedirectory.example.com'; my $bind_dn = 'CN=ldapuser,OU=Utility,OU=Users,DC=example,DC=com'; my $bind_pw = 'secret'; my $base_dn = 'DC=example,DC=com'; my $search_filter = '(!(userAccountControl:1.2.840.113556.1.4.803:=2))'; my $ldap = Net::LDAP->new($host) or die "$@"; print 'Active Directory Password Changer $Id$'; die "REMOTE_USER not set.\n" unless $ENV{'REMOTE_USER'}; unless ( $ENV{'HTTPS'} eq 'on' ) { print "This script requires HTTPS.\n"; die; } print "Logged in as $ENV{REMOTE_USER}. LDAP URI is $host.
\n"; $cgi = new CGI::Lite; %form = $cgi->parse_form_data; if ($form{'op'} eq 'submit') { my $user_dn = $form{'dn'}; my $user_pw = $form{'oldpassword'}; my $newpw1 = $form{'newpw1'}; my $newpw2 = $form{'newpw2'}; die 'Password mismatch.' unless ( $newpw1 eq $newpw2 ); die 'No DN specified.' unless $user_dn; print "Attempting to bind as $user_dn ... "; my $mesg = $ldap->bind($user_dn, password => $user_pw); if ($mesg->is_error) { my $err = "Error attempting to bind as $user_dn: ". $mesg->error; print "
$err
"; die $err; } print " OK.
\n"; my $oldUniPW = $charmap->tou('"'.$user_pw.'"')->byteswap()->utf16(); my $newUniPW = $charmap->tou('"'.$newpw1.'"')->byteswap()->utf16(); print "Attempting to modify password for $user_dn ... "; $mesg = $ldap->modify($user_dn, changes => [ delete => [ unicodePwd => $oldUniPW ], add => [ unicodePwd => $newUniPW ] ]); print "OK
\n"; # $mesg->is_error && die ('Modify error: '. $mesg->error); if ($mesg->is_error) { my $err = 'Modify error: '. $mesg->error; print "
$err
\n"; die $err; } $ldap->unbind(); print "VICTORY!! Successfully changed password for user $ENV{REMOTE_USER}, DN $user_dn, msg ID: ".$mesg->mesg_id ."\n"; } else { # bind as dummy to lookup the user's DN my $mesg = $ldap->bind($bind_dn, password => $bind_pw ); $mesg->is_error && die ('Bind error: '. $mesg->error); $mesg = $ldap->search( base => $base_dn, scope => 'sub', attrs => ['dn','cn','mail'], filter => "(&(samaccountname=$ENV{REMOTE_USER})$search_filter)" ); $mesg->is_error && die ( 'Search failed: '. $mesg->error ); $mesg->count || die ("Search for user '$ENV{REMOTE_USER}' returned no results."); my $entry = $mesg->entry( 0 ); $ldap->unbind(); print "LDAP DN for user '$ENV{REMOTE_USER}': ".$entry->dn.''; print "
\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "
Current password for $ENV{REMOTE_USER}:
New password for $ENV{REMOTE_USER}:
New password for $ENV{REMOTE_USER} again:
\n"; } print '';