Re: Web based password changer
On Fri, Jan 23, 2004 at 12:17:00AM -0700, Will Aoki wrote:
> I've attached a slightly cleaned-up version of the password changer that
Perhaps this time I'll remember to attach the file *and* the mailing
list won't reject it...
--
William Aoki KD7YAF waoki@umnh.utah.edu /"\ ASCII Ribbon Campaign
\ / No HTML in mail or news!
X
/ \
#!/usr/bin/perl -w -T
#
# Password changer
#
# written by Will Aoki <waoki AT umnh.utah.edu>
#
# $Id: passwdchanger.cgi,v 1.2 2004/01/23 06:53:26 waoki Exp $
use strict;
use CGI qw/:standard -nodebug/;
use CGI::Carp;
use FileHandle;
use IO::Select;
use IPC::Open2;
use IPC::Open3;
use Sys::Syslog;
### Config goes here:
my $config_ldapserver = "ldap.umnh.utah.edu";
my $config_basedn = ",ou=People,dc=umnh,dc=utah,dc=edu";
my $config_title = "UMNH Password Changer";
### End of config
#carp "Starting!\n";
# Setup
my $pwchars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./';
my @pwchars = split //, $pwchars;
undef $pwchars;
#print $#pwchars, "\n";
# Returns Windows passwds
sub mkntpwd ($) {
my $p = open2(*Reader, *Writer, '/usr/local/sbin/mkntpwd', '-f', '-');
print Writer $_[0], "\n";
#close Writer;
my $pass = <Reader>;
chomp $pass;
my @passes = split /:/, $pass;
return @passes;
}
#print mkntpwd("HI"), "\n";
#print mkntpwd("IH"), "\n";
sub changepass ($$$$) {
my $user = $_[0];
my $oldpass = $_[1];
my $newpass = $_[2];
my $newpassconfirm = $_[3];
if ($user !~ /^[a-zA-Z][a-zA-Z0-9]*$/) {
sleep 4;
return "user name contains illegal symbols";
}
if ($oldpass eq '') {
sleep 4;
return "null password for user $user", "authentication failed";
}
if ($newpass ne $newpassconfirm) {
return "new passwords don't match for user $user", "new passwords don't match";
}
my $salt = '$1$';
for my $i (1..8) {
$salt .= $pwchars[rand($#pwchars + 1)];
}
#print "salt is $salt\n";
my $cryptedpass = crypt($newpass, $salt);
my $lmpasswd;
my $ntpasswd;
($lmpasswd,$ntpasswd) = mkntpwd ($newpass);
my $Reader;
my $Writer;
my $Error;
my $p = open3($Writer, $Reader, $Error, '/usr/bin/ldapmodify', '-ZZ', '-h', $config_ldapserver, '-D', "uid=$user" . $config_basedn, '-x', '-W');
my $select = IO::Select->new($Reader, $Error);
my $line;
my $sock;
my $timeout = time() + 1;
do {
my @ready = $select->can_read(5);
foreach my $s (@ready) {
# foreach is implicity local!
$sock = $s;
if ($sock == $Reader) {
last;
} else {
# something on wrong stream
$line = '';
my $l;
my $read = 0;
do {
$read = sysread($sock, $l, 1024);
$line .= $l;
} while (defined $read && ($read != 0 ^ $read != 1024));
syslog('err', "Error: ldapmodify said \"%s\" on stderr while we expected a password prompt", $line);
}
}
} while ((!defined $sock || $Reader != $sock) && time() < $timeout);
if ($sock != $Reader) {
return "Timed out waiting for ldapmodify for user $user", "internal error 3: timed out";
}
sysread($Reader, $line, 30);
if ($line !~ /^Enter LDAP Password:/) {
return "internal error 1";
} else {
print $Writer "$oldpass\n";
}
$timeout = time() + 2;
do {
my @ready = $select->can_read(2);
foreach my $s (@ready) {
$sock = $s;
#if ($sock == $Error) {
$line = '';
my $l;
while (sysread($sock, $l, 1024)) {
$line .= $l;
}
sleep 4;
return "bad password for user $user: $line", "authentication failed";
#}
}
} while (time() < $timeout);
syslog("notice", "authenticated user $user");
print $Writer "dn: uid=$user,ou=People,dc=umnh,dc=utah,dc=edu\n";
print $Writer "userPassword: {CRYPT}$cryptedpass\n";
print $Writer "lmPassword: $lmpasswd\n";
print $Writer "ntPassword: $ntpasswd\n";
print $Writer "\n";
close $Writer;
$timeout = time() + 5;
do {
my @ready = $select->can_read(5);
foreach my $s (@ready) {
$sock = $s;
#if ($sock == $Reader) {
$line = '';
my $l;
while (sysread($sock, $l, 1024)) {
$line .= $l;
}
if ($line =~ /^\s*modifying entry/) {
syslog("notice", "Set new password for user $user");
return 1;
} else {
return "Modify failed for user $user: $line", "internal error 2";
}
#}
}
} while (time() < $timeout);
return "Timed out modifying for user $user", "internal error 4: timed out";
#return 1;
}
#changepass ('a', 'b', 'c');
sub printmenu {
my $c = $_[0];
shift @_;
print $c->header();
print $c->start_html(-title=>$config_title, -color=>"black", -bgcolor=>"white");
print $c->h1($config_title);
if ($#_ >= 0) {
foreach my $e (@_) {
print $c->p("<font color=\"red\">Error: $e</font>");
}
}
print $c->start_form(-method=>"POST");
print $c->table({-border=>'none'},
$c->Tr([
$c->td(["Username: ", $c->textfield(-name=>"username")]),
$c->td(["Old password: ", $c->password_field(-name=>"oldpass", -override=>1)]),
$c->td(["New password: ", $c->password_field(-name=>"newpass1", -override=>1)]),
$c->td(["Confirm new password: ", $c->password_field(-name=>"newpass2", -override=>1)]),
])
);
print $c->submit(-name=>"submitbutton", -value=>"Change password", -content=>"Change password");
print $c->end_form();
print $c->end_html();
print "\n";
}
#printmenu($q);
sub printsuccess ($) {
my $c = $_[0];
print $c->header();
print $c->start_html(-title=>$config_title, -color=>"black", -bgcolor=>"white");
print $c->h1($config_title);
print $c->p("Your password has been changed.");
print $c->p($c->a({-href=>"/"}, "Return"));
print $c->end_html();
print "\n";
}
sub doaction($) {
my $q = $_[0];
my $user = $q->param("username");
my $oldpass = $q->param("oldpass");
my $newpass1 = $q->param("newpass1");
my $newpass2 = $q->param("newpass2");
my @r = changepass($user, $oldpass, $newpass1, $newpass2);
if ($r[0] eq 1) {
printsuccess($q);
} else {
syslog('warning', '%s failed to change password: %s', $q->remote_host(), $r[0]);
if (defined $r[1]) {
printmenu($q, $r[1]);
} else {
printmenu($q, $r[0]);
}
}
}
#my $q = new CGI;
#printsuccess($q)
my $q = new CGI;
openlog("webpwchanger", "pid", "LOG_AUTH");
if (defined $q->param('submitbutton')) {
doaction($q);
} else {
printmenu($q);
}
Reply to: