Handle hostnames with upper-case letters
[webmin.git] / password_change.cgi
index 130c83c..e4766a4 100755 (executable)
 # password_change.cgi
 # Actually update a user's password by directly modifying /etc/shadow
 
+BEGIN { push(@INC, ".."); };
+use WebminCore;
+
 $ENV{'MINISERV_INTERNAL'} || die "Can only be called by miniserv.pl";
-require './web-lib.pl';
 &init_config();
 &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'});
+       $wuser->{'temppass'} = 0;
+       &acl::modify_user($wuser->{'name'}, $wuser);
+       &reload_miniserv();
+       }
+elsif ($gconfig{'passwd_cmd'}) {
+       # Use some configured command
+       $passwd_cmd = &has_command($gconfig{'passwd_cmd'});
+       $passwd_cmd || &pass_error("The password change command <tt>$gconfig{'passwd_cmd'}</tt> was not found");
+
+       &foreign_require("proc", "proc-lib.pl");
+       &clean_environment();
+       $ENV{'REMOTE_USER'} = $in{'user'};      # some programs need this
+       $passwd_cmd .= " ".quotemeta($in{'user'});
+       ($fh, $fpid) = &proc::pty_process_exec($passwd_cmd, 0, 0);
+       &reset_environment();
+       while(1) {
+               local $rv = &wait_for($fh,
+                          '(new|re-enter).*:',
+                          '(old|current|login).*:',
+                          'pick a password',
+                          'too\s+many\s+failures',
+                          'attributes\s+changed\s+on|successfully\s+changed',
+                          'pick your passwords');
+               $out .= $wait_for_input;
+               sleep(1);
+               if ($rv == 0) {
+                       # Prompt for the new password
+                       syswrite($fh, $in{'new1'}."\n", length($in{'new1'})+1);
+                       }
+               elsif ($rv == 1) {
+                       # Prompt for the old password
+                       syswrite($fh, $in{'old'}."\n", length($in{'old'})+1);
+                       }
+               elsif ($rv == 2) {
+                       # Request for a menu option (SCO?)
+                       syswrite($fh, "1\n", 2);
+                       }
+               elsif ($rv == 3) {
+                       # Failed too many times
+                       last;
+                       }
+               elsif ($rv == 4) {
+                       # All done
+                       last;
+                       }
+               elsif ($rv == 5) {
+                       # Request for a menu option (HP/UX)
+                       syswrite($fh, "p\n", 2);
+                       }
+               else {
+                       last;
+                       }
+               last if (++$count > 10);
+               }
+       $crv = close($fh);
+       sleep(1);
+       waitpid($fpid, 1);
+       if ($? || $count > 10 ||
+           $out =~ /error|failed/i || $out =~ /bad\s+password/i) {
+               &pass_error("<tt>".&html_escape($out)."</tt>");
+               }
+       }
+elsif ($in{'pam'}) {
        # Use PAM to make the change..
        eval "use Authen::PAM;";
        if ($@) {
@@ -101,6 +187,15 @@ else {
        &unlock_file($miniserv{'passwd_file'});
        }
 
+# Change password in Usermin too
+if (&get_product_name() eq 'usermin' &&
+    &foreign_check("changepass")) {
+       # XXX remote user??
+       &foreign_require("changepass", "changepass-lib.pl");
+       &changepass::change_mailbox_passwords(
+               $in{'user'}, $in{'old'}, $in{'new1'});
+       }
+
 # Show ok page
 &header(undef, undef, undef, undef, 1, 1);
 
@@ -111,11 +206,11 @@ print "<center><h3>",&text('password_done', "/"),"</h3></center>\n";
 sub pass_error
 {
 &header(undef, undef, undef, undef, 1, 1);
-print "<hr>\n";
+print &ui_hr();
 
 print "<center><h3>",$text{'password_err'}," : ",@_,"</h3></center>\n";
 
-print "<hr>\n";
+print &ui_hr();
 &footer();
 exit;
 }