[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: