3 # Actually update a user's password by directly modifying /etc/shadow
5 $ENV{'MINISERV_INTERNAL'} || die "Can only be called by miniserv.pl";
6 require './web-lib.pl';
9 &get_miniserv_config(\%miniserv);
10 $miniserv{'passwd_mode'} == 2 || die "Password changing is not enabled!";
13 $in{'new1'} ne '' || &pass_error($text{'password_enew1'});
14 $in{'new1'} eq $in{'new2'} || &pass_error($text{'password_enew2'});
16 # Is this a Webmin user?
17 if (&foreign_check("acl")) {
18 &foreign_require("acl", "acl-lib.pl");
19 ($wuser) = grep { $_->{'name'} eq $in{'user'} } &acl::list_users();
20 if ($wuser->{'pass'} eq 'x') {
21 # A Webmin user, but using Unix authentication
24 elsif ($wuser->{'pass'} eq '*LK*' ||
25 $wuser->{'pass'} =~ /^\!/) {
26 &pass_error("Webmin users with locked accounts cannot change ".
30 if (!$in{'pam'} && !$wuser) {
31 $miniserv{'passwd_cindex'} ne '' && $miniserv{'passwd_mindex'} ne '' ||
32 die "Missing password file configuration";
36 # Update Webmin user's password
37 $enc = &acl::encrypt_password($in{'old'}, $wuser->{'pass'});
38 $enc eq $wuser->{'pass'} || &pass_error($text{'password_eold'});
39 $perr = &acl::check_password_restrictions($in{'user'}, $in{'new1'});
40 $perr && &pass_error(&text('password_enewpass', $perr));
41 $wuser->{'pass'} = &acl::encrypt_password($in{'new1'});
42 $wuser->{'temppass'} = 0;
43 &acl::modify_user($wuser->{'name'}, $wuser);
47 # Use PAM to make the change..
48 eval "use Authen::PAM;";
50 &pass_error(&text('password_emodpam', $@));
53 # Check if the old password is correct
54 $service = $miniserv{'pam'} ? $miniserv{'pam'} : "webmin";
55 $pamh = new Authen::PAM($service, $in{'user'}, \&pam_check_func);
56 $rv = $pamh->pam_authenticate();
57 $rv == PAM_SUCCESS() ||
58 &pass_error($text{'password_eold'});
61 # Change the password with PAM, in a sub-process. This is needed because
62 # the UID must be changed to properly signal to the PAM libraries that
63 # the password change is not being done by the root user.
66 @uinfo = getpwnam($in{'user'});
68 ($>, $<) = (0, $uinfo[2]);
69 $pamh = new Authen::PAM("passwd", $in{'user'}, \&pam_change_func);
70 $rv = $pamh->pam_chauthtok();
73 print TEMP ($messages || $pamh->pam_strerror($rv)),"\n";
80 chop($messages = <TEMP>);
83 $rv == PAM_SUCCESS || &pass_error(&text('password_epam', $messages));
87 # Directly update password file
89 # Read shadow file and find user
90 &lock_file($miniserv{'passwd_file'});
91 $lref = &read_file_lines($miniserv{'passwd_file'});
92 for($i=0; $i<@$lref; $i++) {
93 @line = split(/:/, $lref->[$i], -1);
94 local $u = $line[$miniserv{'passwd_uindex'}];
95 if ($u eq $in{'user'}) {
100 defined($idx) || &pass_error($text{'password_euser'});
102 # Validate old password
103 &unix_crypt($in{'old'}, $line[$miniserv{'passwd_pindex'}]) eq
104 $line[$miniserv{'passwd_pindex'}] ||
105 &pass_error($text{'password_eold'});
107 # Make sure new password meets restrictions
108 if (&foreign_check("changepass")) {
109 &foreign_require("changepass", "changepass-lib.pl");
110 $err = &changepass::check_password($in{'new1'}, $in{'user'});
111 &pass_error($err) if ($err);
113 elsif (&foreign_check("useradmin")) {
114 &foreign_require("useradmin", "user-lib.pl");
115 $err = &useradmin::check_password_restrictions(
116 $in{'new1'}, $in{'user'});
117 &pass_error($err) if ($err);
120 # Set new password and save file
121 $salt = chr(int(rand(26))+65) . chr(int(rand(26))+65);
122 $line[$miniserv{'passwd_pindex'}] = &unix_crypt($in{'new1'}, $salt);
123 $days = int(time()/(24*60*60));
124 $line[$miniserv{'passwd_cindex'}] = $days;
125 $lref->[$idx] = join(":", @line);
127 &unlock_file($miniserv{'passwd_file'});
131 &header(undef, undef, undef, undef, 1, 1);
133 print "<center><h3>",&text('password_done', "/"),"</h3></center>\n";
139 &header(undef, undef, undef, undef, 1, 1);
142 print "<center><h3>",$text{'password_err'}," : ",@_,"</h3></center>\n";
157 $ans = $in{'user'} if ($code == PAM_PROMPT_ECHO_ON());
158 $ans = $in{'old'} if ($code == PAM_PROMPT_ECHO_OFF());
160 push @res, PAM_SUCCESS();
163 push @res, PAM_SUCCESS();
176 if ($code == PAM_PROMPT_ECHO_ON()) {
177 # Assume asking for username
178 push @res, PAM_SUCCESS();
179 push @res, $in{'user'};
181 elsif ($code == PAM_PROMPT_ECHO_OFF()) {
182 # Assume asking for a password (old first, then new)
183 push @res, PAM_SUCCESS();
184 if ($msg =~ /old|current/i) {
185 push @res, $in{'old'};
188 push @res, $in{'new1'};
192 # Some message .. ignore it
193 push @res, PAM_SUCCESS();
197 push @res, PAM_SUCCESS();