From ee997485ff0f56996c2d6e35ff8d4027fa0be209 Mon Sep 17 00:00:00 2001 From: Jamie Cameron Date: Thu, 20 Mar 2008 17:51:05 +0000 Subject: [PATCH] Support for temporary passwords which must be changed at the next login --- acl/CHANGELOG | 1 + acl/acl-lib.pl | 7 ++++-- acl/edit_user.cgi | 7 +++++- acl/lang/en | 1 + acl/save_user.cgi | 10 +++++++-- miniserv.pl | 15 +++++++++---- password_change.cgi | 1 + password_form.cgi | 52 +++++++++++++++++++++++++-------------------- 8 files changed, 62 insertions(+), 32 deletions(-) diff --git a/acl/CHANGELOG b/acl/CHANGELOG index 8e5862525..2e7149c31 100644 --- a/acl/CHANGELOG +++ b/acl/CHANGELOG @@ -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. diff --git a/acl/acl-lib.pl b/acl/acl-lib.pl index 2b29c18a0..052d02d9e 100644 --- a/acl/acl-lib.pl +++ b/acl/acl-lib.pl @@ -43,6 +43,7 @@ while() { $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 { diff --git a/acl/edit_user.cgi b/acl/edit_user.cgi index 9a44cbcc5..931d0e776 100755 --- a/acl/edit_user.cgi +++ b/acl/edit_user.cgi @@ -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 ? "
" : "").$lockbox.$tempbox.$expmsg); # Real name print &ui_table_row($text{'edit_real'}, diff --git a/acl/lang/en b/acl/lang/en index 57b50c0b1..a37f91fad 100644 --- a/acl/lang/en +++ b/acl/lang/en @@ -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 .. diff --git a/acl/save_user.cgi b/acl/save_user.cgi index 68755c2ed..3b52d2561 100755 --- a/acl/save_user.cgi +++ b/acl/save_user.cgi @@ -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); diff --git a/miniserv.pl b/miniserv.pl index 25092eaa0..6603393c1 100755 --- a/miniserv.pl +++ b/miniserv.pl @@ -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() { @@ -3953,6 +3959,7 @@ if ($config{'userfile'}) { } $lastchanges{$user[0]} = $user[6]; $nochange{$user[0]} = $user[9]; + $temppass{$user[0]} = $user[10]; } close(USERS); } diff --git a/password_change.cgi b/password_change.cgi index 377c2bee9..354989cab 100755 --- a/password_change.cgi +++ b/password_change.cgi @@ -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(); } diff --git a/password_form.cgi b/password_form.cgi index 601f950b9..7914de3b3 100755 --- a/password_form.cgi +++ b/password_form.cgi @@ -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 "
\n"; -print "

$text{'password_expired'}

\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 "

\n"; -print "\n"; -print "\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); -print "\n"; -print "\n"; -print "
$text{'password_header'}
\n"; +# Current username +print &ui_table_row($text{'password_user'}, + &html_escape($in{'user'})); -print "\n"; -print "\n"; +# Old password +print &ui_table_row($text{'password_old'}, + &ui_password("old", undef, 20)); -print "\n"; -print "\n"; +# 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)); -print "\n"; -print "\n"; -print "\n"; -print "\n"; - -print "\n"; -print "
$text{'password_user'}",&html_escape($in{'user'}),"
$text{'password_old'}
$text{'password_new1'}
$text{'password_new2'}
\n"; -print "
\n"; -print "

\n"; -print "


\n"; -print "
\n"; +# End of form +print &ui_table_end(); +print &ui_form_end([ [ undef, $text{'password_ok'} ] ]); +print "\n"; print "$text{'password_postfix'}\n"; + &footer();