mirror of
https://github.com/webmin/webmin.git
synced 2025-07-23 00:30:33 +00:00
Start of work on password change API for use by roundcube / etc
This commit is contained in:
@ -29,10 +29,11 @@ $user || &errordie("User $ARGV[0] does not exist");
|
|||||||
$| = 1;
|
$| = 1;
|
||||||
if ($askold) {
|
if ($askold) {
|
||||||
# Ask for the old password
|
# Ask for the old password
|
||||||
|
&foreign_require("useradmin");
|
||||||
print "(current) UNIX password: ";
|
print "(current) UNIX password: ";
|
||||||
$old = <STDIN>;
|
$old = <STDIN>;
|
||||||
$old =~ s/\r|\n//g;
|
$old =~ s/\r|\n//g;
|
||||||
&unix_crypt($old, $user->{'pass'}) eq $user->{'pass'} ||
|
&useradmin::validate_password($old, $user->{'pass'}) ||
|
||||||
&errordie("Old password is incorrect");
|
&errordie("Old password is incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
passwd/change_passwd.cgi
Executable file
51
passwd/change_passwd.cgi
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/local/bin/perl
|
||||||
|
# Change a user's password knowing the old one. For user only via anonymous
|
||||||
|
# API calls.
|
||||||
|
|
||||||
|
require './passwd-lib.pl';
|
||||||
|
&ReadParse();
|
||||||
|
print "Content-type: text/plain\n\n";
|
||||||
|
|
||||||
|
# Validate inputs
|
||||||
|
my $err = &apply_rate_limit($ENV{'REMOTE_ADDR'});
|
||||||
|
&error_exit($err) if ($err);
|
||||||
|
$in{'user'} || &error_exit("Missing user parameter");
|
||||||
|
$in{'old'} || &error_exit("Missing old parameter");
|
||||||
|
$in{'new'} || &error_exit("Missing new parameter");
|
||||||
|
$ENV{'ANONYMOUS_USER'} || &error_exit("Can only be called in anonymous mode");
|
||||||
|
$ENV{'REQUEST_METHOD'} eq 'POST' ||
|
||||||
|
&error_exit("Passwords can only be submitted via POST");
|
||||||
|
&foreign_installed("useradmin") ||
|
||||||
|
&error_exit("Users and Groups module is not supported on this OS");
|
||||||
|
|
||||||
|
# Validate user and pass
|
||||||
|
my $err = &apply_rate_limit($in{'user'});
|
||||||
|
&error_exit($err) if ($err);
|
||||||
|
&foreign_require("useradmin");
|
||||||
|
my $user = &find_user($in{'user'});
|
||||||
|
$user || &error_exit("User does not exist");
|
||||||
|
&useradmin::validate_password($in{'old'}, $user->{'pass'}) ||
|
||||||
|
&error_exit("Incorrect password");
|
||||||
|
my $err = &useradmin::check_password_restrictions(
|
||||||
|
$in{'pass'}, $in{'user'}, $user);
|
||||||
|
&error_exit("Invalid password : $err") if ($err);
|
||||||
|
|
||||||
|
# Do the change
|
||||||
|
&clear_rate_limit($ENV{'REMOTE_ADDR'});
|
||||||
|
&clear_rate_limit($in{'user'});
|
||||||
|
eval {
|
||||||
|
local $main::error_must_die = 1;
|
||||||
|
&change_password($user, $in{'pass'}, 1);
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
&error_exit($@);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print "OK: Password changed for $in{'user'}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub error_exit
|
||||||
|
{
|
||||||
|
print "FAILED: ",join("", @_),"\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
@ -14,6 +14,9 @@ BEGIN { push(@INC, ".."); };
|
|||||||
use WebminCore;
|
use WebminCore;
|
||||||
&init_config();
|
&init_config();
|
||||||
%access = &get_module_acl();
|
%access = &get_module_acl();
|
||||||
|
$rate_limit_file = "$module_var_directory/rate-limit";
|
||||||
|
$rate_limit_timeout = 10*60; # 10 minutes
|
||||||
|
$rate_limit_max = 10;
|
||||||
|
|
||||||
=head2 can_edit_passwd(&user)
|
=head2 can_edit_passwd(&user)
|
||||||
|
|
||||||
@ -80,7 +83,6 @@ sub find_user
|
|||||||
local $mod;
|
local $mod;
|
||||||
foreach $mod ([ "useradmin", "user-lib.pl" ],
|
foreach $mod ([ "useradmin", "user-lib.pl" ],
|
||||||
[ "ldap-useradmin", "ldap-useradmin-lib.pl" ],
|
[ "ldap-useradmin", "ldap-useradmin-lib.pl" ],
|
||||||
# [ "nis", "nis-lib.pl" ],
|
|
||||||
) {
|
) {
|
||||||
next if (!&foreign_installed($mod->[0], 1));
|
next if (!&foreign_installed($mod->[0], 1));
|
||||||
&foreign_require($mod->[0], $mod->[1]);
|
&foreign_require($mod->[0], $mod->[1]);
|
||||||
@ -146,5 +148,49 @@ if ($others) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# apply_rate_limit(key)
|
||||||
|
# Delays for some amount of time based on the key, to prevent brute force attacks
|
||||||
|
sub apply_rate_limit
|
||||||
|
{
|
||||||
|
my ($key) = @_;
|
||||||
|
my $now = time();
|
||||||
|
my %rate;
|
||||||
|
&lock_file($rate_limit_file);
|
||||||
|
&read_file($rate_limit_file, \%rate);
|
||||||
|
$rate{$key."_last"} ||= $now;
|
||||||
|
if ($now - $rate{$key."_last"} > $rate_limit_timeout) {
|
||||||
|
# Time since blocking for this key started as expired
|
||||||
|
delete($rate{$key});
|
||||||
|
delete($rate{$key."_last"});
|
||||||
|
}
|
||||||
|
my $rv;
|
||||||
|
if ($rate{$key} > $rate_limit_max) {
|
||||||
|
$rv = "Too many failures for $key";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sleep($rate{$key} ** 2);
|
||||||
|
$rate{$key}++;
|
||||||
|
}
|
||||||
|
&write_file($rate_limit_file, \%rate);
|
||||||
|
&unlock_file($rate_limit_file);
|
||||||
|
return $rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
# clear_rate_limit(key)
|
||||||
|
# After a successful operation, clear any rate limits for the given key
|
||||||
|
sub clear_rate_limit
|
||||||
|
{
|
||||||
|
my ($key) = @_;
|
||||||
|
my %rate;
|
||||||
|
&lock_file($rate_limit_file);
|
||||||
|
&read_file($rate_limit_file, \%rate);
|
||||||
|
delete($rate{$key});
|
||||||
|
delete($rate{$key."_last"});
|
||||||
|
&write_file($rate_limit_file, \%rate);
|
||||||
|
&unlock_file($rate_limit_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user