passwords (RE-SEND)

Aaron D. Brooks abrooks at css.tayloru.edu
Fri Mar 24 17:54:14 GMT 2000


Sorry for CC:ing the list so you guys didn't get the attachment.
That was silly. Here is the attachment and the description again.
[The attached script was written by Joel Martin.]

+------->

Password synchronizing friends.

Well, it's not pretty or perfect but we have been using the atached
Perl/Expect script pretty nicely here for the last few weeks. The CGI is
used in a series of dynamic and moduled html pages so I can't attach all
of that (it wouldn't be of use anyways). We are running SSL on the web
server so the sessions are encrypted.

The Perl/Expect scripts are sort of hairy to go through but aren't really
that bad. If you run in a different environment (RH6.1/Apache/PHP/NIS) you
may have to do anything from changing the variables at the top of the
script to reworking the expect statements. It might be nice at some point
to create a config type file where you can put the Expect strings and
swich on them acording to the versions of yppasswd and smbpasswd but we
are just using this for here right now.

As for the running environment the script only needs the following:

* Run as 'nobody' or equivalent (What?? No root?? Sorry. ;)
* CPAN Expect Perl module
* An html form which passes variables as listed at the top of the CGI
* A dynamic html form (e.g PHP) or CGI to post responses back to
	(i.e. the CGI passes error messages back to the web page --
	we solve this by simply using one PHP page for both of the
	above requirements)
* This doesn't need to be on either the NIS server or the smbserver
* It does require the 'yppasswd' and 'smbpasswd' commands

If people find this useful I can chuck it out on a web page here to be
downloaded. I hope 8Kb (~12Kb once it get's MIMEd) is not too much to post
to the list. _Some_ people post their whole conf files and logs so I
figure I should be safe. :)

This probably wasn't clear at all. If you have any questions feel free to
e-mail me personally and, if it's convenient, cc: jmartin at css.tayloru.edu,
as he wrote the CGI.

Hope this helps someone,

-Aaron

P.S. As this script is currently written it will only change synchronized
passwords. We have it bomb out if the two aren't the same. It's easier
that way. -A. 

+------->
Aaron D. Brooks,  765 . 998 . 5168
Computing Systems Resource Manager
Taylor University,  CSS Department
abrooks [SHIFT"2"] css.tayloru.edu

-------------- next part --------------
#!/usr/bin/perl
# Written by Joel Martin <jmartin at css.tayloru.edu>
# 2000-02-17 for Taylor University CSS Department
#
# Global password changing script. Changes both the NIS password
# database (yppasswd) and the samba database (smbpasswd -r)

use Expect;	# Requires the Expect Perl Module

# Incoming Variable Field Names from Web Form:
#
# username --> Username
# old --> Old password
# new1 --> New password
# new2 --> New password again to verify

$error_email="admin\@css.tayloru.edu";
$smbserver="samson";
$sendmailpath="/usr/sbin/sendmail";
$yppasswdpath="/usr/bin/yppasswd";
$smbpasswdpath="/usr/bin/smbpasswd";
$htmlpage="index.php3?page=password";
$exptimeout=10;

$Expect::Log_Stdout=0; # 1 will listen in on the conversation
$html = "";  # Message placeholder

if (! &VerifyForm() ) {
  &return_msg (2, "You must use the correct submission form.");
}

&ParseForm();

### Get the data from the form submission
$username = $values{username};
chomp($username);
if ($username eq "" || $username eq "0") {
  &return_msg (2, "You must enter your username");
}
$oldpassword = $values{old};
$newpassword1 = $values{new1};
$newpassword2 = $values{new2};
# Now see if yppasswd will swallow the purple rotting salmon
if ($newpassword1 ne $newpassword2) {
  &return_msg (2, "Your new password didn't match the retype");
}


### Check existence of both password programs (yppasswd, smbpasswd)
$yppasswd =Expect->spawn("/bin/su - $username -c ${yppasswdpath}","");
$smbpasswd =Expect->spawn("/bin/su - $username -c \"${smbpasswdpath} -r ${smbserver}\"");

### First check our connection to yppasswd
if ($position =$yppasswd->expect($exptimeout, '-re','su: user [^ ]* does not exist', '-re','Password:')) {
  ### We can connect to yppasswd
  if ($position == 1) {
    ### "su" said that the username didn't exist
    &return_msg (2, "The username \"$username\" doesn't exist");
  }
  print $yppasswd $oldpassword."\n";
  if ($position =$yppasswd->expect($exptimeout, '-re','su: incorrect password', '-re','Please enter old password:')) {
    ### "su" said that the username didn't exist
    if ($position ==1) {
      &return_msg (2, "Your current password is incorrect");
    }
  } else {
    ### hmmm, we shouldn't ever get here, otherwise it means yppasswd broke or changed it's output
    &return_msg(2,"Bad internal error #jdm1");
  }
} else {
  ### We can't connect to yppasswd
  &return_msg(2,"Couldn't connect to yppasswd: ".$yppasswd->exp_error());
}

### Next check our connection to smbpasswd
if ($position =$smbpasswd->expect($exptimeout, '-re','su: user [^ ]* does not exist', '-re','Password:')) {
  ### We can connect to smbpasswd
  if ($position == 1) {
    ### We should never get here. 
    ### "su" said that the username didn't exist but it said it did exist the first time.
    $html .= "That username existed once but not the second time<br>";
    $html .= "Bad internal error #jdm2";
    &return_msg(2,$html);
  }
  print $smbpasswd $oldpassword."\n";
  if (($position,$error,$matched,$before,$after) =$yppasswd->expect($exptimeout, '-re','su: incorrect password', '-re','Old SMB password:')) {
    ### We should get here. That means the password worked once for "su" but not this time
    ### "su" said that the username didn't exist
    if ($position ==1) {
      $html .= "Your old password didn't match<br>";
      $html .= "Bad internal error #jdm3";
      &return_msg(2,$html);
    }
  } else {
    ### hmmm, we shouldn't ever get here, otherwise it means smbpasswd broke or changed it's output
    &return_msg(2,"Bad internal error #jdm4");
  }
} else {
  ### We can't connect to smbpasswd
  &return_msg(2,"Couldn't connect to smbpasswd: ".$smbpasswd->exp_error());
}


### Do the standard password rigamoral. No soft paper chunks.
print $yppasswd "$oldpassword\r";
if (! $yppasswd->expect($exptimeout, '-re','enter new password')) {
  ### This shouldn't happen because we already verified the password via "su"
  $html .= "The password you typed didn't match your old password<br>";
  $html .= "Bad internal error #jdm5";
  &return_msg(2,$html);
}

### User inputs new password and we pass it on to yppasswd. It has the most
### rigorous password check so will just check the password with it.
print $yppasswd "$newpassword1\r";
if (! $yppasswd->expect($exptimeout, '-re','retype new password')) {
  $html .= "The password you typed was too simple or the same as your current password. It must be at least 6 characters long, ";
  $html .= "use both letters and numbers, not be based on dictionary words and can not be the same as your current password";
  &return_msg(2,$html);
}

### Now pass the new password on to yp
print $yppasswd "$newpassword2\r";
if (! $yppasswd->expect($exptimeout, '-re','has been changed')) {
  $hmtl .= "For some reason the NIS password update couldn't be finished<br>";
  $html .= "Bad internal error #jdm6";
  &return_msg(2,$html);
}

### Hopefully smbpasswd doesn't have any problems otherwise we have foofoo
### update anomalies. And if so we e-mail the admin noting the username
print $smbpasswd "$oldpassword\r";
if (! $smbpasswd->expect($exptimeout, '-re','New SMB password:')) {
  $mail_text = "Subject: Password update problem for $username\n\n";
  $mail_text.= $username."'s password was updated in NIS but not SMB\n";
  `echo -e "$mail_text" | ${sendmailpath} ${error_email}`;

  $html .= "You probably have a password mismatch in the databases. ";
  $html .= "Please bring this issue to a systems administrator";
  &return_msg(2,$html);
}
print $smbpasswd "$newpassword1\r";
if (! $smbpasswd->expect($exptimeout, '-re','Retype new SMB password:')) {
  $mail_text = "Subject: Password update problem for $username\n\n";
  $mail_text.= $username."'s password was updated in NIS but not SMB\n";
  `echo -e "$mail_text" | ${sendmailpath} ${error_email}`;

  $html .= "You probably have a password mismatch in the databases. ";
  $html .= "Please bring this issue to a systems administrator";
  &return_msg(2,$html);
}
print $smbpasswd "$newpassword2\r";
if (! $smbpasswd->expect($exptimeout, '-re','changed')) {
  $mail_text = "Subject: Password update problem for $username\n\n";
  $mail_text.= $username."'s password was updated in NIS but not SMB\n";
  `echo -e "$mail_text" | ${sendmailpath} ${error_email}`;

  $html .= "You probably have a password mismatch in the databases. ";
  $html .= "Please bring this issue to a systems administrator";
  &return_msg(2,$html);
}

&return_msg(1,"Your password has been successfully changed");
# Terminate Now


# Send back a message to the password changes screen
sub return_msg
{
      ($status, $text) = @_;

      if ($yppasswd) { $yppasswd->hard_close(); }
      if ($smbpasswd) { $smbpasswd->hard_close(); }

      chomp $text;
      $encoded ="";

      for ($i=0; $i<length $text; $i++) {
	$char = substr($text,$i,1);
	if ($char=~/[A-Za-z0-9]/) { $encoded .= $char; } 
	else { $encoded .=sprintf("%%%x",ord $char); }
      }	

      print "Location: ${htmlpage}&status=${status}&message=${encoded}\n\n";
      exit 1;
}


# This is not used in the web version
sub getpassword
{
  local $localpassword;

  # First we have to initialize STDIN in to an expect object.
  $stdin=Expect->exp_init(\*STDIN);
  # Now turn off echoing
  $stdin->exp_stty('-echo');
  # The easy way to do this is:
  $localpassword=<STDIN>;
  chop $localpassword;
  # Turn echo back on
  $stdin->exp_stty('echo');
  # print that newline that wasn't echoed
  print "\n";

  return $localpassword;
}


sub VerifyForm
{
        local($bad, $contentType, $requestMethod, $result);
        $bad = 0;
        $contentType = $ENV{"CONTENT_TYPE"};
        if ($contentType ne "application/x-www-form-urlencoded") {
                $bad = 1;
        }
        $requestMethod = $ENV{"REQUEST_METHOD"};
        if ($requestMethod ne "POST") {
                $bad = 1;
        }

        $result = ! $bad;
}

sub ParseForm
{
        local($fields, $name, $value, $data);

        read(STDIN, $data, $ENV{"CONTENT_LENGTH"});
        @fields = split(/&/, $data);

        foreach $item (@fields) {
                ($name, $value) = split(/=/, $item);
                $name = &UnescapeString($name);
                $value = &UnescapeString($value);
                $values{$name} = $value;
        }
}

sub UnescapeString
{
        local($s) = $_[0];
        local($pos, $ascii);
        $s =~ s/\+/ /g;
        $pos = 0;
        while (($pos = index($s, "%", $pos)) != -1) {
                $ascii = hex(substr($s, $pos + 1, 2));
                substr($s, $pos, 3) = pack("c", $ascii);
        }
        $s;
}



More information about the samba-ntdom mailing list