Completed password change lockout function
authorJamie Cameron <jcameron@webmin.com>
Thu, 13 Sep 2007 20:55:21 +0000 (20:55 +0000)
committerJamie Cameron <jcameron@webmin.com>
Thu, 13 Sep 2007 20:55:21 +0000 (20:55 +0000)
acl/edit_pass.cgi
acl/lang/en
acl/save_pass.cgi
miniserv.pl
password_change.cgi

index d9595e3..94142e3 100644 (file)
@@ -25,6 +25,11 @@ print &ui_table_row($text{'pass_maxdays'},
        &ui_opt_textbox("maxdays", $miniserv{'pass_maxdays'}, 5,
                        $text{'pass_nomaxdays'})." ".$text{'pass_days'});
 
+# Days before lockout
+print &ui_table_row($text{'pass_lockdays'},
+       &ui_opt_textbox("lockdays", $miniserv{'pass_lockdays'}, 5,
+                       $text{'pass_nolockdays'})." ".$text{'pass_days'});
+
 # Disallow use of username
 print &ui_table_row($text{'pass_nouser'},
        &ui_yesno_radio("nouser", $miniserv{'pass_nouser'}));
index b98dd75..ae99d7c 100644 (file)
@@ -338,6 +338,7 @@ pass_minsize=Minimum password length
 pass_nominsize=No minimum
 pass_regexps=Regular expressions passwords must match
 pass_maxdays=Days before password must be changed
+pass_lockdays=Days before un-changed password locks account
 pass_nomaxdays=Change never required
 pass_nouser=Disallow passwords containing username?
 pass_nodict=Disallow dictionary word passwords?
@@ -348,6 +349,7 @@ pass_pass=passwords
 pass_err=Failed to save password restrictions
 pass_eminsize=Missing or non-numeric minimum password length
 pass_emaxdays=Missing or non-numeric number of days before changing
+pass_elockdays=Missing or non-numeric number of days before account is locked
 pass_eoldblock=Missing or non-numeric number of old passwords to reject
 
 cpass_minsize=Must be at least $1 letters long
index 05288c8..1ff33bd 100644 (file)
@@ -22,6 +22,13 @@ else {
        $in{'maxdays'} =~ /^\d+$/ || &error($text{'pass_emaxdays'});
        $miniserv{'pass_maxdays'} = $in{'maxdays'};
        }
+if ($in{'lockdays_def'}) {
+       delete($miniserv{'pass_lockdays'});
+       }
+else {
+       $in{'lockdays'} =~ /^\d+$/ || &error($text{'pass_elockdays'});
+       $miniserv{'pass_lockdays'} = $in{'lockdays'};
+       }
 $miniserv{'pass_nouser'} = $in{'nouser'};
 $miniserv{'pass_nodict'} = $in{'nodict'};
 if ($in{'oldblock_def'}) {
index 4407158..c0d0528 100755 (executable)
@@ -2899,8 +2899,27 @@ elsif ($canmode == 0) {
        }
 elsif ($canmode == 1) {
        # Attempt Webmin authentication
-       return $users{$webminuser} eq &unix_crypt($pass, $users{$webminuser}) ?
-               ( $user, 0, 0 ) : ( undef, 0, 0 );
+       if ($users{$webminuser} eq &unix_crypt($pass, $users{$webminuser})) {
+               # Password is valid .. but check for expiry
+               local $lc = $lastchanges{$user};
+               if ($config{'pass_maxdays'} && $lc) {
+                       local $daysold = (time() - $lc)/(24*60*60);
+                       print DEBUG "maxdays=$config{'pass_maxdays'} daysold=$daysold\n";
+                       if ($config{'pass_lockdays'} &&
+                           $daysold > $config{'pass_lockdays'}) {
+                               # So old that the account is locked
+                               return ( undef, 0, 0 );
+                               }
+                       elsif ($daysold > $config{'pass_maxdays'}) {
+                               # Password has expired
+                               return ( $user, 1, 0 );
+                               }
+                       }
+               return ( $user, 0, 0 );
+               }
+       else {
+               return ( undef, 0, 0 );
+               }
        }
 elsif ($canmode == 2 || $canmode == 3) {
        # Attempt PAM or passwd file authentication
@@ -3842,6 +3861,7 @@ undef(%allow);
 undef(%deny);
 undef(%allowdays);
 undef(%allowhours);
+undef(%lastchanges);
 if ($config{'userfile'}) {
        open(USERS, $config{'userfile'});
        while(<USERS>) {
@@ -3865,6 +3885,7 @@ if ($config{'userfile'}) {
                if ($user[5] =~ /hours\s+(\d+)\.(\d+)-(\d+).(\d+)/) {
                        $allowhours{$user[0]} = [ $1*60+$2, $3*60+$4 ];
                        }
+               $lastchanges{$user[0]} = $user[6];
                }
        close(USERS);
        }
index 130c83c..377c2be 100755 (executable)
@@ -8,16 +8,41 @@ require './web-lib.pl';
 &ReadParse();
 &get_miniserv_config(\%miniserv);
 $miniserv{'passwd_mode'} == 2 || die "Password changing is not enabled!";
-if (!$in{'pam'}) {
-       $miniserv{'passwd_cindex'} ne '' && $miniserv{'passwd_mindex'} ne '' || 
-               die "Missing password file configuration";
-       }
 
 # Validate inputs
 $in{'new1'} ne '' || &pass_error($text{'password_enew1'});
 $in{'new1'} eq $in{'new2'} || &pass_error($text{'password_enew2'});
 
-if ($in{'pam'}) {
+# Is this a Webmin user?
+if (&foreign_check("acl")) {
+       &foreign_require("acl", "acl-lib.pl");
+       ($wuser) = grep { $_->{'name'} eq $in{'user'} } &acl::list_users();
+       if ($wuser->{'pass'} eq 'x') {
+               # A Webmin user, but using Unix authentication
+               $wuser = undef;
+               }
+       elsif ($wuser->{'pass'} eq '*LK*' ||
+              $wuser->{'pass'} =~ /^\!/) {
+               &pass_error("Webmin users with locked accounts cannot change ".
+                           "their passwords!");
+               }
+       }
+if (!$in{'pam'} && !$wuser) {
+       $miniserv{'passwd_cindex'} ne '' && $miniserv{'passwd_mindex'} ne '' || 
+               die "Missing password file configuration";
+       }
+
+if ($wuser) {
+       # Update Webmin user's password
+       $enc = &acl::encrypt_password($in{'old'}, $wuser->{'pass'});
+       $enc eq $wuser->{'pass'} || &pass_error($text{'password_eold'});
+       $perr = &acl::check_password_restrictions($in{'user'}, $in{'new1'});
+       $perr && &pass_error(&text('password_enewpass', $perr));
+       $wuser->{'pass'} = &acl::encrypt_password($in{'new1'});
+       &acl::modify_user($wuser->{'name'}, $wuser);
+       &reload_miniserv();
+       }
+elsif ($in{'pam'}) {
        # Use PAM to make the change..
        eval "use Authen::PAM;";
        if ($@) {