Support for temporary passwords which must be changed at the next login
authorJamie Cameron <jcameron@webmin.com>
Thu, 20 Mar 2008 17:51:05 +0000 (17:51 +0000)
committerJamie Cameron <jcameron@webmin.com>
Thu, 20 Mar 2008 17:51:05 +0000 (17:51 +0000)
acl/CHANGELOG
acl/acl-lib.pl
acl/edit_user.cgi
acl/lang/en
acl/save_user.cgi
miniserv.pl
password_change.cgi
password_form.cgi

index 8e58625..2e7149c 100644 (file)
@@ -46,3 +46,4 @@ Updated the user interface to use the Webmin UI library.
 Fixed the display of modules granted to groups.
 Added a per-user option to opt out of forced password changes after a certain number of days.
 A human-readable description of the password restrictions regular expression can be entered, for use in error messages.
+Webmin users can now be given temporary passwords, which they are forced to change at the next login.
index 2b29c18..052d02d 100644 (file)
@@ -43,6 +43,7 @@ while(<PWFILE>) {
                $user{'olds'} = [ split(/\s+/, $user[7]) ];
                $user{'minsize'} = $user[8];
                $user{'nochange'} = int($user[9]);
+               $user{'temppass'} = int($user[10]);
                $user{'modules'} = $acl{$user[0]};
                $user{'lang'} = $gconfig{"lang_$user[0]"};
                $user{'notabs'} = $gconfig{"notabs_$user[0]"};
@@ -135,7 +136,8 @@ push(@times, "hours", $user{'hoursfrom'}."-".$user{'hoursto'})
        $user{'lastchange'},":",
        join(" ", @{$user{'olds'}}),":",
        $user{'minsize'},":",
-       $user{'nochange'},
+       $user{'nochange'},":",
+       $user{'temppass'},
        "\n");
 &close_tempfile(PWFILE);
 &unlock_file($miniserv{'userfile'});
@@ -238,7 +240,8 @@ foreach (@pwfile) {
                        $user{'lastchange'},":",
                        join(" ", @{$user{'olds'}}),":",
                        $user{'minsize'},":",
-                       $user{'nochange'},
+                       $user{'nochange'},":",
+                       $user{'temppass'},
                        "\n");
                }
        else {
index 9a44cbc..931d0e7 100755 (executable)
@@ -114,6 +114,10 @@ if ($passmode == 1) {
        $lockbox = &ui_checkbox("lock", 1, $text{'edit_templock'},
                                       $user{'pass'} =~ /^\!/ ? 1 : 0);
        }
+if ($passmode != 3 && $passmode != 4) {
+       $tempbox = &ui_checkbox("temp", 1, $text{'edit_temppass'},
+                               $user{'temppass'});
+       }
 if ($user{'lastchange'} && $miniserv{'pass_maxdays'}) {
        $daysold = int((time() - $user{'lastchange'})/(24*60*60));
        if ($miniserv{'pass_lockdays'} &&
@@ -134,7 +138,8 @@ if ($user{'lastchange'} && $miniserv{'pass_maxdays'}) {
        }
 print &ui_table_row($text{'edit_pass'},
        &ui_select("pass_def", $passmode, \@opts)." ".
-       &ui_password("pass", undef, 25).$lockbox.$expmsg);
+       &ui_password("pass", undef, 25).
+       ($lockbox || $tempbox ? "<br>" : "").$lockbox.$tempbox.$expmsg);
 
 # Real name
 print &ui_table_row($text{'edit_real'},
index 57b50c0..a37f91f 100644 (file)
@@ -83,6 +83,7 @@ edit_rbacdeny0=RBAC only controls selected module ACLs
 edit_rbacdeny1=RBAC controls all modules and ACLs
 edit_special=Special
 edit_templock=Temporarily locked
+edit_temppass=Force change at next login
 edit_days=Allowed days of the week
 edit_alldays=Every day
 edit_seldays=Only selected days ..
index 68755c2..3b52d25 100755 (executable)
@@ -232,8 +232,11 @@ if ($in{'pass_def'} == 0) {
        $in{'pass'} =~ /:/ && &error($text{'save_ecolon'});
        $user{'pass'} = &encrypt_password($in{'pass'});
        $user{'sync'} = 0;
-       $perr = &check_password_restrictions($in{'name'}, $in{'pass'});
-       $perr && &error(&text('save_epass', $perr));
+       if (!$in{'temp'}) {
+               # Check password quality, unless this is a temp password
+               $perr = &check_password_restrictions($in{'name'}, $in{'pass'});
+               $perr && &error(&text('save_epass', $perr));
+               }
        }
 elsif ($in{'pass_def'} == 1) {
        # No change in password
@@ -304,6 +307,9 @@ elsif ($in{'lock'} && $user{'pass'} !~ /^\!/ && $in{'pass_def'} <= 1) {
        $user{'pass'} = "!".$user{'pass'};
        }
 
+# Check for force change
+$user{'temppass'} = $in{'temp'};
+
 if ($in{'old'}) {
        # update user and all ACLs
        &modify_user($in{'old'}, \%user);
index 25092ea..6603393 100755 (executable)
@@ -2944,7 +2944,7 @@ sub urlize {
 
 # validate_user(username, password, host)
 # Checks if some username and password are valid. Returns the modified username,
-# the expired flag, and the non-existence flag
+# the expired / temp pass flag, and the non-existence flag
 sub validate_user
 {
 local ($user, $pass, $host) = @_;
@@ -2978,6 +2978,10 @@ elsif ($canmode == 1) {
                                # Password has expired
                                return ( $user, 1, 0 );
                                }
+                       elsif ($temppass{$user}) {
+                               # Temporary password - force change now
+                               return ( $user, 2, 0 );
+                               }
                        }
                return ( $user, 0, 0 );
                }
@@ -3522,13 +3526,14 @@ if ($ok && (!$expired ||
        return 0;
        }
 elsif ($ok && $expired &&
-       $config{'passwd_mode'} == 2) {
-       # Login was ok, but password has expired. Need
+       ($config{'passwd_mode'} == 2 || $expired == 2)) {
+       # Login was ok, but password has expired or was temporary. Need
        # to force display of password change form.
        $validated = 1;
        $authuser = undef;
        $querystring = "&user=".&urlize($vu).
-                      "&pam=".$use_pam;
+                      "&pam=".$use_pam.
+                      "&expired=".$expired;
        $method = "GET";
        $queryargs = "";
        $page = $config{'password_form'};
@@ -3928,6 +3933,7 @@ undef(%allowdays);
 undef(%allowhours);
 undef(%lastchanges);
 undef(%nochange);
+undef(%temppass);
 if ($config{'userfile'}) {
        open(USERS, $config{'userfile'});
        while(<USERS>) {
@@ -3953,6 +3959,7 @@ if ($config{'userfile'}) {
                        }
                $lastchanges{$user[0]} = $user[6];
                $nochange{$user[0]} = $user[9];
+               $temppass{$user[0]} = $user[10];
                }
        close(USERS);
        }
index 377c2be..354989c 100755 (executable)
@@ -39,6 +39,7 @@ if ($wuser) {
        $perr = &acl::check_password_restrictions($in{'user'}, $in{'new1'});
        $perr && &pass_error(&text('password_enewpass', $perr));
        $wuser->{'pass'} = &acl::encrypt_password($in{'new1'});
+       $wuser->{'temppass'} = 0;
        &acl::modify_user($wuser->{'name'}, $wuser);
        &reload_miniserv();
        }
index 601f950..7914de3 100755 (executable)
@@ -5,39 +5,45 @@
 $ENV{'MINISERV_INTERNAL'} || die "Can only be called by miniserv.pl";
 require './web-lib.pl';
 &init_config();
+require './ui-lib.pl';
 &ReadParse();
 &header(undef, undef, undef, undef, 1, 1);
 
 print "<center>\n";
-print "<h3>$text{'password_expired'}</h3><p>\n";
-
+if ($in{'expired'} == 2) {
+       print &ui_subheading($text{'password_temp'});
+       }
+else {
+       print &ui_subheading($text{'password_expired'});
+       }
+
+# Start of the form
 print "$text{'password_prefix'}\n";
-print "<form action=$gconfig{'webprefix'}/password_change.cgi method=post>\n";
-print "<input type=hidden name=user value='",&html_escape($in{'user'}),"'>\n";
-print "<input type=hidden name=pam value='",&html_escape($in{'pam'}),"'>\n";
-
-print "<table border width=40%>\n";
-print "<tr $tb> <td><b>$text{'password_header'}</b></td> </tr>\n";
-print "<tr $cb> <td align=center><table cellpadding=3>\n";
-
-print "<tr> <td><b>$text{'password_user'}</b></td>\n";
-print "<td><tt>",&html_escape($in{'user'}),"</tt></td> </tr>\n";
-
-print "<tr> <td><b>$text{'password_old'}</b></td>\n";
-print "<td><input name=old size=20 type=password></td> </tr>\n";
-
-print "<tr> <td><b>$text{'password_new1'}</b></td>\n";
-print "<td><input name=new1 size=20 type=password></td> </tr>\n";
-print "<tr> <td><b>$text{'password_new2'}</b></td>\n";
-print "<td><input name=new2 size=20 type=password></td> </tr>\n";
-
-print "<tr> <td colspan=2 align=center><input type=submit ",
-      "value='$text{'password_ok'}'>\n";
-print "<input type=reset value='$text{'password_clear'}'><br>\n";
-print "</td> </tr>\n";
-print "</table></td></tr></table><p>\n";
-print "<hr>\n";
-print "</form></center>\n";
+print &ui_form_start("$gconfig{'webprefix'}/password_change.cgi", "post");
+print &ui_hidden("user", $in{'user'});
+print &ui_hidden("pam", $in{'pam'});
+print &ui_hidden("expired", $in{'expired'});
+print &ui_table_start($text{'password_header'}, "width=50% style='width:50%'", 2);
+
+# Current username
+print &ui_table_row($text{'password_user'},
+       &html_escape($in{'user'}));
+
+# Old password
+print &ui_table_row($text{'password_old'},
+       &ui_password("old", undef, 20));
+
+# New password, twice
+print &ui_table_row($text{'password_new1'},
+       &ui_password("new1", undef, 20));
+print &ui_table_row($text{'password_new2'},
+       &ui_password("new2", undef, 20));
+
+# End of form
+print &ui_table_end();
+print &ui_form_end([ [ undef, $text{'password_ok'} ] ]);
+print "</center>\n";
 print "$text{'password_postfix'}\n";
+
 &footer();