Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Change Active Directory password from a web browser.
# Evan Hoffman (
# 13 January, 2010
# Base functionality from:
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://';
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 '<html><title>Active Directory Password Changer $Id$</title><body>';
die "REMOTE_USER not set.\n" unless $ENV{'REMOTE_USER'};
unless ( $ENV{'HTTPS'} eq 'on' ) {
print "<b>This script requires HTTPS.</b>\n";
print "Logged in as <b>$ENV{REMOTE_USER}</b>. LDAP URI is <b>$host</b>.<br>\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 <b>$user_dn</b> ... ";
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 "<pre>$err</pre>";
die $err;
print " OK.<br>\n";
my $oldUniPW = $charmap->tou('"'.$user_pw.'"')->byteswap()->utf16();
my $newUniPW = $charmap->tou('"'.$newpw1.'"')->byteswap()->utf16();
print "Attempting to modify password for <b>$user_dn</b> ... ";
$mesg = $ldap->modify($user_dn,
changes => [
delete => [ unicodePwd => $oldUniPW ],
add => [ unicodePwd => $newUniPW ] ]);
print "OK<br>\n";
# $mesg->is_error && die ('Modify error: '. $mesg->error);
if ($mesg->is_error) {
my $err = 'Modify error: '. $mesg->error;
print "<pre>$err</pre>\n";
die $err;
print "<blink>VICTORY!!</blink> Successfully changed password for user $ENV{REMOTE_USER}, DN <b>$user_dn</b>, 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 );
print "LDAP DN for user '$ENV{REMOTE_USER}': <b>".$entry->dn.'</b>';
print "<form action='$ENV{REQUEST_URI}' METHOD='POST'>\n";
print "<input type='hidden' name='op' value='submit'>\n";
print "<input type='hidden' name='dn' value='".$entry->dn."'>\n";
print "<table border='0'>\n";
print "<tr><td>Current password for <b>$ENV{REMOTE_USER}</b>:</td><td><input type='password' name='oldpassword' size='30'></td></tr>\n";
print "<tr><td>New password for <b>$ENV{REMOTE_USER}</b>:</td><td><input type='password' name='newpw1' size='30'></td></tr>\n";
print "<tr><td>New password for <b>$ENV{REMOTE_USER}</b> <i>again</i>:</td><td><input type='password' name='newpw2' size='30'></td></tr>\n";
print "<tr><td colspan='2'><input type='submit' name='submit' value='Change Password'><input type='reset' value='Reset Form'></td></tr>\n";
print "</form>\n";
print "</table>\n";
print '</body></html>';
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment