[Da-tools-commits] ./debian/userdir-ldap-cgi r109: Implement password quality checking in update

Peter Palfrader peter at palfrader.org
Mon Sep 15 13:26:52 UTC 2008


------------------------------------------------------------
revno: 109
committer: Peter Palfrader <peter at palfrader.org>
branch nick: userdir-ldap-cgi
timestamp: Mon 2008-09-15 15:26:52 +0200
message:
  Implement password quality checking in update
modified:
  Util.pm
  update.cgi
-------------- next part --------------
=== modified file 'Util.pm'
--- a/Util.pm	2008-05-25 16:27:04 +0000
+++ b/Util.pm	2008-09-15 13:26:52 +0000
@@ -1,9 +1,14 @@
 # -*- perl -*-x
+
+# Copyright (c) 1999-2006  Debian Admin Team Members and Developers    (taken from debian/copyright in 2008 by weasel)
+# Copyright (c) 2002, 2003, 2004, 2008 Peter Palfrader
+
 package Util;
 
 use strict;
 use Date::Manip qw(ParseDate);
 use Net::LDAP qw(:all);
+use English;
 
 my $blocksize = 8; # A blowfish block is 8 bytes
 my $configfile = "/etc/userdir-ldap/userdir-ldap.conf";
@@ -353,4 +358,122 @@
     exit(1);
   };
 };
+
+sub readwrite3($$$$) {
+  my ($in, $inputfd, $stdoutfd, $stderrfd) = @_;
+
+  #Echolot::Log::trace("Entering readwrite_gpg.");
+
+  local $INPUT_RECORD_SEPARATOR = undef;
+  my $sout = IO::Select->new();
+  my $sin = IO::Select->new();
+  my $offset = 0;
+
+  #Echolot::Log::trace("input is $inputfd; output is $stdoutfd; err is $stderrfd; status is ".(defined $statusfd ? $statusfd : 'undef').".");
+
+  $inputfd->blocking(0);
+  $stdoutfd->blocking(0);
+  $stderrfd->blocking(0);
+  $sout->add($stdoutfd);
+  $sout->add($stderrfd);
+  $sin->add($inputfd);
+
+  my ($stdout, $stderr) = ("", "", "");
+
+  my ($readyr, $readyw);
+  while ($sout->count() > 0 || (defined($sin) && ($sin->count() > 0))) {
+    #Echolot::Log::trace("select waiting for ".($sout->count())." fds.");
+    ($readyr, $readyw, undef) = IO::Select::select($sout, $sin, undef, 42);
+    #Echolot::Log::trace("ready: write: ".(defined $readyw ? scalar @$readyw : 'none')."; read: ".(defined $readyr ? scalar @$readyr : 'none'));
+    for my $wfd (@$readyw) {
+      #Echolot::Log::trace("writing to $wfd.");
+      my $written = 0;
+      if ($offset != length($in)) {
+        $written = $wfd->syswrite($in, length($in) - $offset, $offset);
+      }
+      unless (defined ($written)) {
+        #Echolot::Log::warn("Error while writing to GnuPG: $!");
+        close $wfd;
+        $sin->remove($wfd);
+        $sin = undef;
+      } else {
+        $offset += $written;
+        if ($offset == length($in)) {
+          #Echolot::Log::trace("writing to $wfd done.");
+          close $wfd;
+          $sin->remove($wfd);
+          $sin = undef;
+        }
+      }
+    }
+
+    next unless (defined(@$readyr)); # Wait some more.
+
+    for my $rfd (@$readyr) {
+      if ($rfd->eof) {
+        #Echolot::Log::trace("reading from $rfd done.");
+        $sout->remove($rfd);
+        close($rfd);
+        next;
+      }
+      #Echolot::Log::trace("reading from $rfd.");
+      if ($rfd == $stdoutfd) {
+        $stdout .= <$rfd>;
+        next;
+      }
+      if ($rfd == $stderrfd) {
+        $stderr .= <$rfd>;
+        next;
+      }
+    }
+  }
+  #Echolot::Log::trace("readwrite_gpg done.");
+  return ($stdout, $stderr);
+};
+
+sub checkPasswordQuality($$$) {
+  my ($pw, $oldpw, $ldapelements) = @_;
+  my ($stdinR, $stdinW) = (IO::Handle->new(), IO::Handle->new());
+  my ($stdoutR, $stdoutW) = (IO::Handle->new(), IO::Handle->new());
+  my ($stderrR, $stderrW) = (IO::Handle->new(), IO::Handle->new());
+  pipe $stdinR, $stdinW;
+  pipe $stdoutR, $stdoutW;
+  pipe $stderrR, $stderrW;
+
+  my $pid = fork();
+  unless (defined $pid) {
+    return (2, "Could not fork: $!");
+  };
+  unless ($pid) { # child
+    $stdinW->close;
+    $stdoutR->close;
+    $stderrR->close;
+    close STDIN;
+    close STDOUT;
+    close STDERR;
+    open (STDIN, "<&".$stdinR->fileno) or warn ("Cannot dup stdinR (fd ".$stdinR->fileno.") as STDIN: $!");
+    open (STDOUT, ">&".$stdoutW->fileno) or warn ("Cannot dup stdoutW (fd ".$stdoutW->fileno.") as STDOUT: $!");
+    open (STDERR, ">&".$stderrW->fileno) or warn ("Cannot dup stderrW (fd ".$stderrW->fileno.") as STDERR: $!");
+    { exec('/usr/lib/userdir-ldap-cgi/password-qualify-check'); }
+    $stderrW->print("Could not exec password-qualify-check: $!\n");
+    exit(1);
+  };
+  $stdinR->close;
+  $stdoutW->close;
+  $stderrW->close;
+
+  $oldpw = '' unless defined $oldpw;
+  my $out = join("\n", $pw, $oldpw, @$ldapelements)."\n";
+  my ($stdout, $stderr) = readwrite3($out, $stdinW, $stdoutR, $stderrR);
+  waitpid $pid, 0;
+  
+  my $exitcode = $? >> 8;
+  if ($exitcode == 0 && $stdout eq '' && $stderr eq '') {
+    return (0, "ok");
+  } elsif ($exitcode == 1 && $stderr eq '') {
+    return (1, $stdout);
+  } else {
+    return (2, "check exited with exit code $exitcode, said '$stdout' on stdout, and '$stderr' on stderr.");
+  };
+};
 1;

=== modified file 'update.cgi'
--- a/update.cgi	2008-09-14 22:48:37 +0000
+++ b/update.cgi	2008-09-15 13:26:52 +0000
@@ -119,8 +119,8 @@
                    . '>female</select>';
   my $confirmstring = '';
   my $sudopassword = '';
-  for my $entry (@{$entry->{'sudopassword'}}) {
-    my ($uuid, $status, $hosts, $crypted) = ($entry =~ /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}) (confirmed:[0-9a-f]{40}|unconfirmed) ([a-z0-9.,*]+) ([^ ]+)$/);
+  for my $e(@{$entry->{'sudopassword'}}) {
+    my ($uuid, $status, $hosts, $crypted) = ($e =~ /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}) (confirmed:[0-9a-f]{40}|unconfirmed) ([a-z0-9.,*]+) ([^ ]+)$/);
     unless (defined $uuid) {
       $sudopassword .= "<tr><td>Unparseable line!</td></tr>\n";
       next;
@@ -165,6 +165,14 @@
   }
   close F;
 } else {
+  my @ldapinfo_for_pwcheck;
+  for my $a (qw{cn sn md gecos uid}) {
+    for my $e (@{$entry->{$a}}) {
+      push @ldapinfo_for_pwcheck, $e;
+    }
+  }
+
+
   # Actually update stuff...
   my ($newpassword, $newstaddress);
   
@@ -179,7 +187,13 @@
     if ($query->param('newpass') ne $query->param('newpassvrfy')) {
       # passwords don't match...
       &Util::HTMLError("The passwords you specified do not match. Please go back and try again.");
-    }    
+    }
+
+    my ($r, $msg) = &Util::checkPasswordQuality($query->param('newpass'), undef, [@ldapinfo_for_pwcheck]);
+    if ($r) {
+      &Util::HTMLError("Password check failed: $msg.  Please go back and try again.");
+    }
+
     # create a md5 crypted password
     $newpassword = '{crypt}'.crypt($query->param('newpass'), &Util::CreateCryptSalt(1));
     
@@ -237,13 +251,23 @@
   my $newsudo;
   my $newsudo_hosts;
   if ($query->param('newsudopass') && $query->param('newsudopassvrfy')) {
-    if ($query->param('newsudopass') ne $query->param('newsudopassvrfy')) {
-      &Util::HTMLError("The sudo passwords you specified do not match. Please go back and try again.");
-    }
     my $host = $query->param('newsudopass-host');
     if ($host =~ /[^a-z0-9.-]/ and $host ne '*') {
       &Util::HTMLError("The sudo host has weird characters '$host'.");
     }
+
+    if ($query->param('newsudopass') ne $query->param('newsudopassvrfy')) {
+      &Util::HTMLError("The sudo passwords you specified do not match. Please go back and try again.");
+    }
+
+    my $ldappass = $password;
+    $ldappass = $query->param('newpass') if $query->param('newpass');
+    push @ldapinfo_for_pwcheck, $host, split(/\./, $host);
+    my ($r, $msg) = &Util::checkPasswordQuality($query->param('newsudopass'), $ldappass, [@ldapinfo_for_pwcheck]);
+    if ($r) {
+      &Util::HTMLError("Password check failed for new sudo pass: $msg.  Please go back and try again.");
+    }
+
     # create a md5 crypted password
     my $newsudopassword = crypt($query->param('newsudopass'), &Util::CreateCryptSalt(1));
     my $ug = new Data::UUID;



More information about the Da-tools-commits mailing list